openapi: 3.0.3
info:
  title: IntelXview AI Control Plane API
  version: 1.0.0
  description: |
    Public APIs for the IntelXview AI Control Plane.

    Surfaces:
      - Tenant Admin API: https://api.limitedview.co.uk/tenant/v1
      - Management/Agent API: https://api.limitedview.co.uk/mgmt/v1

    All write operations MUST be idempotent when an `Idempotency-Key` header is provided.
    All endpoints MUST return `X-Request-Id`.

    ## Authoritative source

    This hand-curated YAML is a **human-readable subset** maintained for buyers
    and integrators. The **authoritative, complete contract** is the spec the
    FastAPI app generates from the live route definitions. It is committed to
    the repository at `openapi/openapi_generated.json` and is also served at
    `GET /openapi.json` when `ACP_ENABLE_API_DOCS` is enabled (disabled in
    production by default). When this YAML and the generated spec disagree, the
    generated spec wins.

    The generated spec includes the External Agent Governance routes:
    `POST /tenant/v1/agent-events`, `POST /tenant/v1/agent-execute`,
    `POST /tenant/v1/tool-decisions`, `POST /tenant/v1/data-decisions`,
    `POST /tenant/v1/egress-decisions`, `POST /tenant/v1/tool-gateway/execute`,
    `GET /tenant/v1/approvals/{approval_id}`, and the management approval
    decision route `POST /mgmt/v1/tenants/{tenant_id}/approvals/{approval_id}/decision`.

servers:
  - url: https://api.limitedview.co.uk

tags:
  - name: TenantAuth
  - name: Tenant
  - name: Users
  - name: ServiceAccounts
  - name: Projects
  - name: Policies
  - name: Models
  - name: Routing
  - name: Secrets
  - name: Execution
  - name: Audit
  - name: Billing
  - name: MgmtTenants
  - name: MgmtBilling
  - name: MgmtOps
  - name: Impersonation

security:
  - OAuth2: []
  - ApiKeyAuth: []

paths:
  /tenant/v1/auth/token:
    post:
      tags: [TenantAuth]
      summary: Exchange a service account key for an access token
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [service_account_id]
              properties:
                service_account_id:
                  type: string
                  description: Service account identifier
      responses:
        '200':
          description: Token issued
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TokenResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }

  /tenant/v1/tenant:
    get:
      tags: [Tenant]
      summary: Get tenant profile
      responses:
        '200':
          description: Tenant profile
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Tenant' }
        '401': { $ref: '#/components/responses/Unauthorized' }
    patch:
      tags: [Tenant]
      summary: Update tenant profile (within allowed bounds)
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/TenantUpdateRequest' }
      responses:
        '200':
          description: Updated tenant profile
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Tenant' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }

  /tenant/v1/users:
    get:
      tags: [Users]
      summary: List users
      description: >-
        Returns all visible users in the tenant. Internal seed placeholder rows
        (used for bootstrap gating) are excluded from the response. Seed
        placeholder user IDs are also not retrievable via PATCH or DELETE;
        attempts to modify them return 404.
      parameters:
        - $ref: '#/components/parameters/PageLimit'
        - $ref: '#/components/parameters/PageCursor'
      responses:
        '200':
          description: Users page (excludes internal seed placeholder rows)
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PagedUsers' }
        '401': { $ref: '#/components/responses/Unauthorized' }
    post:
      tags: [Users]
      summary: Create user
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/UserCreateRequest' }
      responses:
        '201':
          description: User created
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/User' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }

  /tenant/v1/users/{user_id}:
    patch:
      tags: [Users]
      summary: Update user
      parameters:
        - name: user_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/UserUpdateRequest' }
      responses:
        '200':
          description: User updated
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/User' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      tags: [Users]
      summary: Delete user (soft-delete by default)
      parameters:
        - name: user_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '204':
          description: User deleted
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }

  /tenant/v1/service-accounts:
    post:
      tags: [ServiceAccounts]
      summary: Create a service account
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ServiceAccountCreateRequest' }
      responses:
        '201':
          description: Service account created
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ServiceAccount' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }

  /tenant/v1/service-accounts/{service_account_id}/keys:
    post:
      tags: [ServiceAccounts]
      summary: Create an API key for a service account
      description: Returned key is shown once. Store it securely.
      parameters:
        - name: service_account_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '201':
          description: Key created
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ServiceAccountKeyCreated' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }

  /tenant/v1/projects:
    get:
      tags: [Projects]
      summary: List projects
      parameters:
        - $ref: '#/components/parameters/PageLimit'
        - $ref: '#/components/parameters/PageCursor'
      responses:
        '200':
          description: Projects page
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PagedProjects' }
    post:
      tags: [Projects]
      summary: Create project
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ProjectCreateRequest' }
      responses:
        '201':
          description: Project created
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Project' }

  /tenant/v1/projects/{project_id}:
    patch:
      tags: [Projects]
      summary: Update project
      parameters:
        - name: project_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ProjectUpdateRequest' }
      responses:
        '200':
          description: Project updated
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Project' }
    delete:
      tags: [Projects]
      summary: Delete project (soft-delete by default)
      parameters:
        - name: project_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '204':
          description: Project deleted
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }

  /tenant/v1/policies/tenant:
    get:
      tags: [Policies]
      summary: Get tenant policy (active + versions)
      responses:
        '200':
          description: Tenant policy
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/TenantPolicyBundle' }

  /tenant/v1/policies/tenant/versions:
    post:
      tags: [Policies]
      summary: Create new tenant policy version
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PolicyVersionCreateRequest' }
      responses:
        '201':
          description: Policy version created
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PolicyVersion' }

  /tenant/v1/policies/tenant/activate/{version_id}:
    post:
      tags: [Policies]
      summary: Activate a tenant policy version
      parameters:
        - name: version_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: Activated
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PolicyActivationResult' }

  /tenant/v1/policies/projects/{project_id}:
    get:
      tags: [Policies]
      summary: Get project policy overrides (active + versions)
      parameters:
        - name: project_id
          in: path
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Project policy bundle
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ProjectPolicyBundle' }

  /tenant/v1/policies/projects/{project_id}/versions:
    post:
      tags: [Policies]
      summary: Create new project policy override version
      parameters:
        - name: project_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PolicyVersionCreateRequest' }
      responses:
        '201':
          description: Project policy override created
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PolicyVersion' }

  /tenant/v1/policies/projects/{project_id}/activate/{version_id}:
    post:
      tags: [Policies]
      summary: Activate a project policy override version
      parameters:
        - name: project_id
          in: path
          required: true
          schema: { type: string }
        - name: version_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: Activated
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PolicyActivationResult' }

  /tenant/v1/models:
    get:
      tags: [Models]
      summary: List models/providers available to the tenant
      responses:
        '200':
          description: Model catalog
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ModelCatalog' }

  /tenant/v1/routing/rules:
    patch:
      tags: [Routing]
      summary: Update routing rules (policy-constrained)
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/RoutingRulesUpdateRequest' }
      responses:
        '200':
          description: Updated routing rules
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RoutingRules' }

  /tenant/v1/secrets/providers:
    get:
      tags: [Secrets]
      summary: List configured provider secrets (metadata only)
      responses:
        '200':
          description: Provider secrets metadata
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema:
                type: object
                properties:
                  providers:
                    type: array
                    items: { $ref: '#/components/schemas/ProviderSecretMeta' }

  /tenant/v1/secrets/providers/{provider}:
    put:
      tags: [Secrets]
      summary: Store tenant provider secret (Vault-backed)
      parameters:
        - name: provider
          in: path
          required: true
          schema: { $ref: '#/components/schemas/ModelProviderId' }
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ProviderSecretPutRequest' }
      responses:
        '204':
          description: Stored
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
    delete:
      tags: [Secrets]
      summary: Delete tenant provider secret
      parameters:
        - name: provider
          in: path
          required: true
          schema: { $ref: '#/components/schemas/ModelProviderId' }
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '204':
          description: Deleted
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }

  /tenant/v1/authorize:
    post:
      tags: [Execution]
      summary: PDP authorization decision (for client-side precheck)
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/AuthorizeRequest' }
      responses:
        '200':
          description: Decision
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AuthorizeDecision' }

  /tenant/v1/execute:
    post:
      tags: [Execution]
      summary: Execute an AI request (governed)
      description: |
        Flow: Identity → PDP → Policy constraints → Redaction → Region enforcement → Routing → Provider call → Audit → Response.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ExecuteRequest' }
      responses:
        '200':
          description: Execution result
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ExecuteResponse' }
        '402':
          description: Payment required / insufficient credits
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ErrorResponse' }

  /tenant/v1/audit:
    get:
      tags: [Audit]
      summary: Query audit log
      parameters:
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
        - name: action
          in: query
          schema: { type: string }
        - $ref: '#/components/parameters/PageLimit'
        - $ref: '#/components/parameters/PageCursor'
      responses:
        '200':
          description: Audit events
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PagedAuditEvents' }

  /tenant/v1/audit/export:
    post:
      tags: [Audit]
      summary: Export audit events to an object store (Spaces)
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/AuditExportRequest' }
      responses:
        '202':
          description: Export job accepted
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AuditExportAccepted' }

  /tenant/v1/billing/subscription:
    get:
      tags: [Billing]
      summary: Get current subscription status
      responses:
        '200':
          description: Subscription status
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SubscriptionStatus' }

  /tenant/v1/billing/credits:
    get:
      tags: [Billing]
      summary: Get prepaid credit balance
      responses:
        '200':
          description: Credit balance
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CreditBalance' }

  /tenant/v1/billing/credit-packs:
    get:
      tags: [Billing]
      summary: List fixed prepaid credit packs and Stripe checkout availability
      responses:
        '200':
          description: Available prepaid credit packs
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CreditPackCatalog' }

  /tenant/v1/billing/checkout-session:
    post:
      tags: [Billing]
      summary: Create an embedded Stripe checkout session for a fixed credit pack
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/BillingCheckoutSessionRequest' }
      responses:
        '200':
          description: Embedded checkout session created
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BillingCheckoutSessionResponse' }

  /tenant/v1/billing/checkout-sessions/{session_id}:
    get:
      tags: [Billing]
      summary: Retrieve checkout session status and any applied funding ledger entry
      parameters:
        - name: session_id
          in: path
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Checkout session status
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BillingCheckoutSessionStatus' }

  /tenant/v1/billing/credits/purchase-intent:
    post:
      tags: [Billing]
      summary: Create a Stripe Checkout/PaymentIntent for prepaid credits
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CreditPurchaseIntentRequest' }
      responses:
        '200':
          description: Stripe intent created
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CreditPurchaseIntentResponse' }

  /tenant/v1/billing/ledger:
    get:
      tags: [Billing]
      summary: List recent funding ledger events for the tenant
      responses:
        '200':
          description: Funding ledger events
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/FundingLedger' }

  /tenant/v1/billing/threshold-alerts:
    get:
      tags: [Billing]
      summary: Get the tenant low-credit threshold alert policy
      responses:
        '200':
          description: Threshold alert policy
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ThresholdAlertResponse' }
    post:
      tags: [Billing]
      summary: Set the tenant low-credit threshold alert policy
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ThresholdAlertRequest' }
      responses:
        '200':
          description: Threshold alert policy updated
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ThresholdAlertResponse' }
    delete:
      tags: [Billing]
      summary: Clear the tenant low-credit threshold alert policy
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: Threshold alert policy cleared
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ThresholdAlertResponse' }

  /tenant/v1/billing/auto-topup:
    get:
      tags: [Billing]
      summary: Get the tenant auto top-up policy
      responses:
        '200':
          description: Auto top-up policy
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AutoTopupPolicyResponse' }
    post:
      tags: [Billing]
      summary: Set the tenant auto top-up policy
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/AutoTopupPolicyRequest' }
      responses:
        '200':
          description: Auto top-up policy updated
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AutoTopupPolicyResponse' }
    delete:
      tags: [Billing]
      summary: Disable the tenant auto top-up policy
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: Auto top-up policy disabled
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AutoTopupPolicyResponse' }

  # =====================
  # Management / Agent API
  # =====================
  /mgmt/v1/tenants:
    post:
      tags: [MgmtTenants]
      summary: Create tenant
      security:
        - OAuth2: [mgmt:tenants:write]
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/MgmtTenantCreateRequest' }
      responses:
        '201':
          description: Tenant created
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Tenant' }

  /mgmt/v1/tenants/{tenant_id}:
    patch:
      tags: [MgmtTenants]
      summary: Update tenant plan/limits
      security:
        - OAuth2: [mgmt:tenants:write]
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/MgmtTenantUpdateRequest' }
      responses:
        '200':
          description: Updated
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Tenant' }
    delete:
      tags: [MgmtTenants]
      summary: Delete tenant (soft-delete)
      security:
        - OAuth2: [mgmt:tenants:write]
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '202':
          description: Deletion scheduled
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/MgmtTenantDeletionAccepted' }

  /mgmt/v1/tenants/{tenant_id}/suspend:
    post:
      tags: [MgmtTenants]
      summary: Suspend tenant
      security:
        - OAuth2: [mgmt:tenants:write]
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '204':
          description: Suspended
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }

  /mgmt/v1/tenants/{tenant_id}/reactivate:
    post:
      tags: [MgmtTenants]
      summary: Reactivate tenant
      security:
        - OAuth2: [mgmt:tenants:write]
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '204':
          description: Reactivated
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }

  /mgmt/v1/tenants/{tenant_id}/stripe/customer:
    post:
      tags: [MgmtBilling]
      summary: Create Stripe customer for tenant
      security:
        - OAuth2: [mgmt:billing:write]
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '201':
          description: Customer created
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StripeCustomerRef' }

  /mgmt/v1/tenants/{tenant_id}/stripe/subscription:
    post:
      tags: [MgmtBilling]
      summary: Create subscription for tenant
      security:
        - OAuth2: [mgmt:billing:write]
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/MgmtSubscriptionCreateRequest' }
      responses:
        '201':
          description: Subscription created
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StripeSubscriptionRef' }
    patch:
      tags: [MgmtBilling]
      summary: Update subscription
      security:
        - OAuth2: [mgmt:billing:write]
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/MgmtSubscriptionUpdateRequest' }
      responses:
        '200':
          description: Updated
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StripeSubscriptionRef' }

  /mgmt/v1/stripe/webhooks:
    post:
      tags: [MgmtBilling]
      summary: Receive Stripe webhooks (idempotent)
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: Raw Stripe event
      responses:
        '200':
          description: Received
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }

  /mgmt/v1/tenants/{tenant_id}/usage:
    get:
      tags: [MgmtOps]
      summary: Get tenant usage summary (metering)
      security:
        - OAuth2: [mgmt:ops:read]
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema: { type: string }
        - name: from
          in: query
          schema: { type: string, format: date-time }
        - name: to
          in: query
          schema: { type: string, format: date-time }
      responses:
        '200':
          description: Usage summary
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/UsageSummary' }

  /mgmt/v1/tenants/{tenant_id}/impersonate:
    post:
      tags: [Impersonation]
      summary: Start controlled impersonation for a tenant (time-limited token)
      security:
        - OAuth2: [mgmt:impersonate]
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason:
                  type: string
                ticket_ref:
                  type: string
      responses:
        '200':
          description: Impersonation token
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ImpersonationToken' }

  /mgmt/v1/tenants/{tenant_id}/impersonate/end:
    post:
      tags: [Impersonation]
      summary: End impersonation session
      security:
        - OAuth2: [mgmt:impersonate]
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema: { type: string }
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '204':
          description: Ended
          headers:
            X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }

components:
  headers:
    X-Request-Id:
      description: Correlation/request id
      schema: { type: string }

  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      schema: { type: string }
      description: Idempotency key for safe retries of write operations
    PageLimit:
      name: limit
      in: query
      required: false
      schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
    PageCursor:
      name: cursor
      in: query
      required: false
      schema: { type: string }

  securitySchemes:
    OAuth2:
      type: oauth2
      description: OAuth2/OIDC access token (Bearer)
      flows:
        authorizationCode:
          authorizationUrl: https://controller.intelxview.com/oauth/authorize
          tokenUrl: https://api.limitedview.co.uk/oauth/token
          scopes:
            tenant:read: Read tenant data
            tenant:write: Modify tenant data
            project:read: Read project data
            project:write: Modify project data
            policy:read: Read policies
            policy:write: Modify policies
            audit:read: Read audit events
            audit:export: Export audit events
            model:read: Read models
            model:write: Modify model availability
            billing:read: Read billing
            billing:write: Modify billing
            execute:ai: Execute AI requests
            mgmt:tenants:write: Manage tenants
            mgmt:billing:write: Manage billing
            mgmt:ops:read: Read ops data
            mgmt:impersonate: Controlled impersonation
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-Api-Key
      description: Service account API key mapped to scoped identity

  responses:
    BadRequest:
      description: Bad request
      headers:
        X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
    Unauthorized:
      description: Unauthorized
      headers:
        X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
    Forbidden:
      description: Forbidden
      headers:
        X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
    NotFound:
      description: Not found
      headers:
        X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
    Conflict:
      description: Conflict
      headers:
        X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }
    RateLimited:
      description: Rate limited
      headers:
        X-Request-Id: { $ref: '#/components/headers/X-Request-Id' }
        Retry-After:
          schema: { type: integer }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/ErrorResponse' }

  schemas:
    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message]
          properties:
            code: { type: string }
            message: { type: string }
            details: { type: object, additionalProperties: true }

    TokenResponse:
      type: object
      required: [access_token, token_type, expires_in]
      properties:
        access_token: { type: string }
        token_type: { type: string, enum: [Bearer] }
        expires_in: { type: integer }
        scope: { type: string }

    Tenant:
      type: object
      required: [tenant_id, name, status, created_at]
      properties:
        tenant_id: { type: string }
        name: { type: string }
        status: { type: string, enum: [active, suspended, deleted] }
        plan: { type: string, nullable: true }
        default_region: { type: string, nullable: true }
        created_at: { type: string, format: date-time }

    TenantUpdateRequest:
      type: object
      properties:
        name: { type: string }
        default_region: { type: string }

    User:
      type: object
      required: [user_id, email, role, created_at]
      properties:
        user_id: { type: string }
        email: { type: string, format: email }
        role: { type: string, enum: [OrgAdmin, ComplianceAdmin, ProjectOwner, User] }
        mfa_enabled: { type: boolean, default: false }
        created_at: { type: string, format: date-time }

    UserCreateRequest:
      type: object
      required: [email, role]
      properties:
        email: { type: string, format: email }
        role: { type: string, enum: [OrgAdmin, ComplianceAdmin, ProjectOwner, User] }

    UserUpdateRequest:
      type: object
      properties:
        role: { type: string, enum: [OrgAdmin, ComplianceAdmin, ProjectOwner, User] }
        mfa_enabled: { type: boolean }

    PagedUsers:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: { $ref: '#/components/schemas/User' }
        next_cursor: { type: string, nullable: true }

    ServiceAccount:
      type: object
      required: [service_account_id, name, scopes, created_at]
      properties:
        service_account_id: { type: string }
        name: { type: string }
        scopes:
          type: array
          items: { type: string }
        created_at: { type: string, format: date-time }

    ServiceAccountCreateRequest:
      type: object
      required: [name, scopes]
      properties:
        name: { type: string }
        scopes:
          type: array
          items: { type: string }

    ServiceAccountKeyCreated:
      type: object
      required: [key_id, api_key]
      properties:
        key_id: { type: string }
        api_key: { type: string, description: Shown once }

    Project:
      type: object
      required: [project_id, name, created_at]
      properties:
        project_id: { type: string }
        name: { type: string }
        sensitivity: { type: string, enum: [LOW, MED, HIGH], default: LOW }
        created_at: { type: string, format: date-time }

    ProjectCreateRequest:
      type: object
      required: [name]
      properties:
        name: { type: string }
        sensitivity: { type: string, enum: [LOW, MED, HIGH] }

    ProjectUpdateRequest:
      type: object
      properties:
        name: { type: string }
        sensitivity: { type: string, enum: [LOW, MED, HIGH] }

    PagedProjects:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: { $ref: '#/components/schemas/Project' }
        next_cursor: { type: string, nullable: true }

    PolicyVersion:
      type: object
      required: [version_id, created_at, policy]
      properties:
        version_id: { type: string }
        created_at: { type: string, format: date-time }
        effective_from: { type: string, format: date-time, nullable: true }
        effective_to: { type: string, format: date-time, nullable: true }
        policy:
          type: object
          additionalProperties: true

    PolicyVersionCreateRequest:
      type: object
      required: [policy]
      properties:
        policy:
          type: object
          additionalProperties: true
        reason:
          type: string
          description: Required for loosening controls

    PolicyActivationResult:
      type: object
      required: [active_version_id]
      properties:
        active_version_id: { type: string }

    TenantPolicyBundle:
      type: object
      required: [active_version_id, versions]
      properties:
        active_version_id: { type: string }
        versions:
          type: array
          items: { $ref: '#/components/schemas/PolicyVersion' }

    ProjectPolicyBundle:
      type: object
      required: [project_id, active_version_id, versions]
      properties:
        project_id: { type: string }
        active_version_id: { type: string, nullable: true }
        versions:
          type: array
          items: { $ref: '#/components/schemas/PolicyVersion' }

    ModelProviderId:
      type: string
      enum: [openai, anthropic, google, deepseek, baidu, alibaba, iflytek, sensetime, moonshot, ollama]

    ProviderSecretMeta:
      type: object
      required: [provider, configured]
      properties:
        provider: { $ref: '#/components/schemas/ModelProviderId' }
        configured: { type: boolean }
        updated_at: { type: string, format: date-time, nullable: true }

    ProviderSecretPutRequest:
      type: object
      required: [secret]
      properties:
        secret:
          type: string
          description: Provider API key/token (stored in Vault; never returned)

    ModelCatalog:
      type: object
      required: [providers]
      properties:
        providers:
          type: array
          items:
            type: object
            required: [provider, models]
            properties:
              provider: { $ref: '#/components/schemas/ModelProviderId' }
              models:
                type: array
                items:
                  type: object
                  required: [model_id, region, enabled]
                  properties:
                    model_id: { type: string }
                    region: { type: string }
                    enabled: { type: boolean }
                    cost_hint_usd_per_1m_tokens: { type: number, nullable: true }

    RoutingRules:
      type: object
      required: [priority]
      properties:
        priority:
          type: array
          description: Ordered priorities
          items:
            type: string
            enum: [cost, latency, quality]
        allowed_regions:
          type: array
          items: { type: string }
          nullable: true
        allowlist_models:
          type: array
          items: { type: string }
          nullable: true

    RoutingRulesUpdateRequest:
      allOf:
        - $ref: '#/components/schemas/RoutingRules'

    AuthorizeRequest:
      type: object
      required: [action]
      properties:
        project_id: { type: string, nullable: true }
        action:
          type: string
          enum: [AI_EXECUTE, PROJECT_READ, PROJECT_WRITE, AUDIT_READ_METADATA, AUDIT_READ_CONTENT, POLICY_VIEW, POLICY_UPDATE_TENANT, POLICY_UPDATE_PROJECT, USER_ASSIGN_PROJECT_ROLE]
        context:
          type: object
          additionalProperties: true

    AuthorizeDecision:
      type: object
      required: [allowed, reason_code, policy_version]
      properties:
        allowed: { type: boolean }
        reason_code: { type: string }
        policy_version:
          type: object
          properties:
            tenant: { type: string }
            project: { type: string, nullable: true }
        enforcement_flags:
          type: object
          additionalProperties: true

    ExecuteRequest:
      type: object
      required: [project_id, input]
      properties:
        project_id: { type: string }
        input:
          type: object
          required: [messages]
          properties:
            messages:
              type: array
              items:
                type: object
                required: [role, content]
                properties:
                  role: { type: string, enum: [system, user, assistant] }
                  content: { type: string }
        options:
          type: object
          additionalProperties: true

    ExecuteResponse:
      type: object
      required: [output, usage]
      properties:
        output:
          type: object
          properties:
            message: { type: string }
        usage:
          type: object
          properties:
            input_tokens: { type: integer }
            output_tokens: { type: integer }
            model_used: { type: string }
            provider: { $ref: '#/components/schemas/ModelProviderId' }
            estimated_cost_usd: { type: number, nullable: true }

    AuditEvent:
      type: object
      required: [event_id, timestamp, actor_id, action, result]
      properties:
        event_id: { type: string }
        timestamp: { type: string, format: date-time }
        actor_id: { type: string }
        actor_type: { type: string, enum: [user, service_account, mgmt_agent] }
        action: { type: string }
        resource: { type: string, nullable: true }
        result: { type: string, enum: [allow, deny] }
        reason_code: { type: string, nullable: true }
        policy_version_tenant: { type: string }
        policy_version_project: { type: string, nullable: true }
        provider: { $ref: '#/components/schemas/ModelProviderId', nullable: true }
        model_used: { type: string, nullable: true }

    PagedAuditEvents:
      type: object
      required: [items]
      properties:
        items:
          type: array
          items: { $ref: '#/components/schemas/AuditEvent' }
        next_cursor: { type: string, nullable: true }

    AuditExportRequest:
      type: object
      required: [from, to]
      properties:
        from: { type: string, format: date-time }
        to: { type: string, format: date-time }
        format: { type: string, enum: [csv, json], default: csv }

    AuditExportAccepted:
      type: object
      required: [job_id]
      properties:
        job_id: { type: string }

    TenantEntitlements:
      type: object
      required: [execution_enabled, reason_code]
      properties:
        execution_enabled: { type: boolean }
        reason_code: { type: string }
        plan_slug: { type: string, nullable: true }
        plan_name: { type: string, nullable: true }
        plan_category: { type: string, enum: [evaluation, paid], nullable: true }
        max_users: { type: integer, nullable: true }
        max_projects: { type: integer, nullable: true }
        routing_profile: { type: string, nullable: true }
        premium_model_access: { type: boolean, nullable: true }
        premium_access_state: { type: string, nullable: true }
        premium_lock_reason: { type: string, nullable: true }
        premium_minimum_balance: { type: integer, nullable: true }
        allowed_model_ids:
          type: array
          items: { type: string }
        upgrade_required: { type: boolean, nullable: true }
        upgrade_message: { type: string, nullable: true }
        upgrade_cta_path: { type: string, nullable: true }
        funding_required: { type: boolean, nullable: true }
        funding_state: { type: string, nullable: true }
        funding_message: { type: string, nullable: true }
        funding_cta_path: { type: string, nullable: true }
        low_credit_warning: { type: boolean, nullable: true }
        low_credit_threshold: { type: integer, nullable: true }
        low_credit_message: { type: string, nullable: true }

    SubscriptionStatus:
      type: object
      required: [tenant_id, plan, status, invoice_status, entitlements]
      properties:
        tenant_id: { type: string }
        plan: { type: string }
        status: { type: string, enum: [active, past_due, canceled, trialing, inactive] }
        invoice_status: { type: string }
        entitlements: { $ref: '#/components/schemas/TenantEntitlements' }
        threshold_alert: { $ref: '#/components/schemas/ThresholdAlert' }
        auto_topup_policy: { $ref: '#/components/schemas/AutoTopupPolicy' }

    CreditBalance:
      type: object
      required: [tenant_id, balance, monthly_usage, monthly_cap]
      properties:
        tenant_id: { type: string }
        balance: { type: integer, description: "Remaining prepaid balance in ACP execution units" }
        monthly_usage: { type: integer }
        monthly_cap: { type: integer }
        currency: { type: string, nullable: true }
        threshold_alert: { $ref: '#/components/schemas/ThresholdAlert' }
        auto_topup_policy: { $ref: '#/components/schemas/AutoTopupPolicy' }

    BillingStripeAvailability:
      type: object
      required: [embedded_checkout_available, live_mode, stub_mode]
      properties:
        publishable_key: { type: string, nullable: true }
        embedded_checkout_available: { type: boolean }
        live_mode: { type: boolean }
        stub_mode: { type: boolean }

    CreditPack:
      type: object
      required: [pack_id, currency, amount_cents, credits]
      properties:
        pack_id: { type: string }
        currency: { type: string }
        amount: { type: string, nullable: true }
        amount_cents: { type: integer }
        credits: { type: integer }
        display_amount: { type: string, nullable: true }
        effective_credit_price_usd: { type: string, nullable: true }
        credits_per_dollar: { type: string, nullable: true }
        label: { type: string, nullable: true }
        best_value: { type: boolean, nullable: true }
        recommended: { type: boolean, nullable: true }

    CreditPackCatalog:
      type: object
      required: [tenant_id, packs, currency, stripe]
      properties:
        tenant_id: { type: string }
        packs:
          type: array
          items: { $ref: '#/components/schemas/CreditPack' }
        pricing:
          type: object
          additionalProperties: { type: string }
        currency: { type: string }
        stripe: { $ref: '#/components/schemas/BillingStripeAvailability' }

    BillingCheckoutSessionRequest:
      type: object
      required: [pack_id]
      properties:
        tenant_id: { type: string, nullable: true }
        pack_id: { type: string }
        return_url: { type: string, nullable: true }

    BillingCheckoutSessionResponse:
      type: object
      required: [tenant_id, pack, session_id, live_mode, stub_mode]
      properties:
        tenant_id: { type: string }
        pack: { $ref: '#/components/schemas/CreditPack' }
        session_id: { type: string }
        client_secret: { type: string, nullable: true }
        checkout_url: { type: string, nullable: true }
        publishable_key: { type: string, nullable: true }
        live_mode: { type: boolean }
        stub_mode: { type: boolean }

    ThresholdAlert:
      type: object
      required: [enabled, threshold, channels]
      properties:
        enabled: { type: boolean }
        threshold: { type: integer }
        channels:
          type: object
          required: [email, in_app]
          properties:
            email: { type: boolean }
            in_app: { type: boolean }
        warning_active: { type: boolean, nullable: true }
        current_balance: { type: integer, nullable: true }
        updated_at: { type: string, nullable: true, format: date-time }
        updated_by: { type: string, nullable: true }

    ThresholdAlertRequest:
      type: object
      required: [threshold]
      properties:
        tenant_id: { type: string, nullable: true }
        threshold: { type: integer }
        email_enabled: { type: boolean, nullable: true }
        in_app_enabled: { type: boolean, nullable: true }

    ThresholdAlertResponse:
      type: object
      required: [tenant_id, threshold_alert]
      properties:
        tenant_id: { type: string }
        threshold_alert: { $ref: '#/components/schemas/ThresholdAlert' }

    AutoTopupPolicy:
      type: object
      required: [enabled, threshold, max_topups_per_24h, max_topups_per_30d]
      properties:
        enabled: { type: boolean }
        threshold: { type: integer }
        pack_id: { type: string, nullable: true }
        max_topups_per_24h: { type: integer }
        max_topups_per_30d: { type: integer }
        payment_method_id: { type: string, nullable: true }
        last_run_at: { type: string, nullable: true, format: date-time }
        last_result: { type: string, nullable: true }
        last_error: { type: string, nullable: true }
        failure_count: { type: integer, nullable: true }
        in_progress: { type: boolean, nullable: true }
        disabled_reason: { type: string, nullable: true }
        updated_at: { type: string, nullable: true, format: date-time }
        updated_by: { type: string, nullable: true }

    AutoTopupPolicyRequest:
      type: object
      required: [threshold, pack_id]
      properties:
        tenant_id: { type: string, nullable: true }
        threshold: { type: integer }
        pack_id: { type: string }
        max_topups_per_24h: { type: integer, nullable: true }
        max_topups_per_30d: { type: integer, nullable: true }

    AutoTopupPolicyResponse:
      type: object
      required: [tenant_id, auto_topup_policy]
      properties:
        tenant_id: { type: string }
        auto_topup_policy: { $ref: '#/components/schemas/AutoTopupPolicy' }

    FundingLedgerEntry:
      type: object
      required: [ledger_id, event_type, source, credits_delta, amount_cents, currency, status]
      properties:
        ledger_id: { type: string }
        event_type: { type: string }
        source: { type: string }
        package_id: { type: string, nullable: true }
        package_snapshot: { type: object, additionalProperties: true, nullable: true }
        stripe_session_id: { type: string, nullable: true }
        stripe_payment_intent_id: { type: string, nullable: true }
        credits_delta: { type: integer }
        amount_cents: { type: integer }
        currency: { type: string }
        status: { type: string }
        description: { type: string, nullable: true }
        initiated_by: { type: string, nullable: true }
        failure_reason: { type: string, nullable: true }
        created_at: { type: string, nullable: true, format: date-time }
        updated_at: { type: string, nullable: true, format: date-time }

    FundingLedger:
      type: object
      required: [tenant_id, items]
      properties:
        tenant_id: { type: string }
        items:
          type: array
          items: { $ref: '#/components/schemas/FundingLedgerEntry' }

    BillingCheckoutSessionStatus:
      type: object
      required: [session_id, balance]
      properties:
        session_id: { type: string }
        status: { type: string, nullable: true }
        payment_status: { type: string, nullable: true }
        applied:
          type: object
          additionalProperties: true
          nullable: true
        ledger_entry: { $ref: '#/components/schemas/FundingLedgerEntry' }
        balance: { type: integer }

    CreditPurchaseIntentRequest:
      type: object
      required: [amount]
      properties:
        tenant_id: { type: string, nullable: true }
        amount: { type: integer, description: Credits to purchase in smallest unit }
        currency: { type: string, default: USD }
        return_url: { type: string, nullable: true }

    CreditPurchaseIntentResponse:
      type: object
      required: [stripe_client_secret]
      properties:
        stripe_client_secret: { type: string }
        session_id: { type: string, nullable: true }
        publishable_key: { type: string, nullable: true }
        pack: { $ref: '#/components/schemas/CreditPack' }

    # Mgmt
    MgmtTenantCreateRequest:
      type: object
      required: [name]
      properties:
        name: { type: string }
        plan: { type: string, nullable: true }
        default_region: { type: string, nullable: true }

    MgmtTenantUpdateRequest:
      type: object
      properties:
        plan: { type: string }
        status: { type: string, enum: [active, suspended] }
        limits:
          type: object
          additionalProperties: true

    MgmtTenantDeletionAccepted:
      type: object
      required: [status]
      properties:
        status: { type: string, enum: [scheduled] }
        retention_days: { type: integer, default: 30 }

    StripeCustomerRef:
      type: object
      required: [stripe_customer_id]
      properties:
        stripe_customer_id: { type: string }

    StripeSubscriptionRef:
      type: object
      required: [stripe_subscription_id]
      properties:
        stripe_subscription_id: { type: string }

    MgmtSubscriptionCreateRequest:
      type: object
      required: [price_id]
      properties:
        price_id: { type: string }

    MgmtSubscriptionUpdateRequest:
      type: object
      properties:
        price_id: { type: string }
        cancel_at_period_end: { type: boolean }

    UsageSummary:
      type: object
      required: [tenant_id, totals]
      properties:
        tenant_id: { type: string }
        totals:
          type: object
          properties:
            requests: { type: integer }
            input_tokens: { type: integer }
            output_tokens: { type: integer }
            cost_usd_estimate: { type: number }

    ImpersonationToken:
      type: object
      required: [access_token, expires_in, tenant_id]
      properties:
        tenant_id: { type: string }
        access_token: { type: string }
        expires_in: { type: integer, default: 900 }

x-limitedview-council:
  description: Council Engine additions for /execute (multi-model deliberation).
  execute_request_additions:
    mode:
      type: string
      enum: [single, council, strict_council]
      default: single
    council:
      type: object
      properties:
        template:
          type: string
          enum: [propose_critic_judge, debate_judge, safety_review]
          default: propose_critic_judge
        participants:
          type: array
          items:
            type: object
            properties:
              role:
                type: string
                enum: [proposer, critic, judge, safety, verifier]
              provider:
                type: string
              model:
                type: string
        thresholds:
          type: object
          properties:
            consensus_min:
              type: number
              minimum: 0
              maximum: 1
              default: 0.6
            safety_pass_required:
              type: boolean
              default: true
