openapi: 3.1.0
info:
  title: Qeet ID — Operations API (audit, billing, analytics)
  version: 0.2.0
  description: |-
    Qeet ID identity platform — full HTTP surface.

    This document is generated to cover EVERY route mounted by the chi router (internal/http/router.go) and is guarded by internal/http/openapi_coverage_test.go, which fails CI if any mounted route is undocumented. Launch-critical families (Auth, OIDC/OAuth, SCIM, SAML, Users, Tenants, RBAC, API Keys) carry fully-described request/response bodies; remaining admin CRUD routes carry accurate path/method/params/security with summarized bodies (noted in each operation's description).

    Auth modes: `bearerAuth` (user/service JWT), `apiKeyAuth` (`Authorization: ApiKey <key>`), `scimBearer` (per-tenant SCIM token), `clientCredentials` (OAuth M2M), and `ssoCookie` (hosted-login SSO session).
servers:
  - url: https://api.id.qeet.in
    description: Production
  - url: https://api.id.staging.qeet.in
    description: Staging
  - url: http://localhost:4001
    description: Local (docker compose)
tags:
  - name: Audit
    description: Append-only, hash-chained audit log query and integrity verification.
  - name: GDPR
    description: Right-to-erasure purge requests.
  - name: Billing
    description: Subscription plans, subscriptions, and invoices.
  - name: Analytics
    description: Per-tenant usage analytics.
  - name: Email Templates
    description: Per-tenant transactional email template overrides.
  - name: Data Retention
    description: Per-tenant auto-purge of soft-deleted users.
  - name: Admin
    description: Operational/admin endpoints (outbox DLQ).
  - name: Health
    description: Liveness, readiness, and Prometheus metrics.
  - name: Rate Limits
    description: Per-tenant rate-limit overrides (tenant / user / API-key buckets).
paths:
  /v1/tenants/{tenantID}/rate-limits:
    get:
      operationId: get_v1_tenants_ByTenantID_rate_limits
      summary: Get rate limits
      description: Returns the effective per-bucket limits (tenant, user, api_key) — the platform defaults merged with any tenant-specific overrides.
      tags:
        - Rate Limits
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      responses:
        '200':
          description: Effective limits
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TenantRateLimits'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    put:
      operationId: put_v1_tenants_ByTenantID_rate_limits
      summary: Set rate limits
      tags:
        - Rate Limits
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TenantRateLimits'
      responses:
        '200':
          description: Updated effective limits
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TenantRateLimits'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/Unprocessable'
    delete:
      operationId: delete_v1_tenants_ByTenantID_rate_limits
      summary: Reset rate limits
      tags:
        - Rate Limits
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      responses:
        '204':
          description: Overrides cleared
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /healthz:
    get:
      operationId: get_healthz
      summary: Liveness
      tags:
        - Health
      security: []
      responses:
        '200':
          description: Alive
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
  /metrics:
    get:
      operationId: get_metrics
      summary: Metrics (Prometheus)
      tags:
        - Health
      security: []
      responses:
        '200':
          description: Prometheus exposition format
          content:
            text/plain:
              schema:
                type: string
  /readyz:
    get:
      operationId: get_readyz
      summary: Readiness
      tags:
        - Health
      security: []
      responses:
        '200':
          description: Ready
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
        '503':
          description: Not ready (a dependency is down)
  /v1/admin/outbox/dlq:
    get:
      operationId: get_v1_admin_outbox_dlq
      summary: List DLQ events
      tags:
        - Admin
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/billing/checkout:
    post:
      operationId: post_v1_tenants_ByTenantID_billing_checkout
      summary: Start checkout
      description: 'Returns a hosted-payment URL to redirect the admin to, or {status:"active"} when the plan is free or no card provider serves the currency (direct activation). Provider routing: INR → Razorpay, else → Stripe. The provider''s webhook later activates the subscription.'
      tags:
        - Billing
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - plan_code
                - currency
                - success_url
                - cancel_url
              properties:
                plan_code:
                  type: string
                currency:
                  type: string
                success_url:
                  type: string
                  format: uri
                cancel_url:
                  type: string
                  format: uri
      responses:
        '200':
          description: Either a checkout URL ({status:"checkout"}) or {status:"active"}
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
  /v1/billing/webhooks/{provider}:
    post:
      operationId: post_v1_billing_webhooks_ByProvider
      summary: Payment webhook
      description: Receives provider payment events. Authenticated by the provider's webhook signature (no user session); on a verified successful payment the referenced checkout is completed idempotently.
      tags:
        - Billing
      security: []
      parameters:
        - name: provider
          in: path
          required: true
          schema:
            type: string
            enum:
              - stripe
              - razorpay
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Event acknowledged
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /v1/billing/plans:
    get:
      operationId: get_v1_billing_plans
      summary: List plans
      tags:
        - Billing
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/gdpr/purge:
    post:
      operationId: post_v1_gdpr_purge
      summary: Create purge request
      tags:
        - GDPR
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
        description: Request body summarized; see the handler for exact fields.
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/gdpr/purge/{id}:
    delete:
      operationId: delete_v1_gdpr_purge_ById
      summary: Cancel purge request
      tags:
        - GDPR
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '204':
          description: No Content
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/analytics/overview:
    get:
      operationId: get_v1_tenants_ByTenantID_analytics_overview
      summary: Tenant analytics overview
      tags:
        - Analytics
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/audit:
    get:
      operationId: get_v1_tenants_ByTenantID_audit
      summary: List audit events
      tags:
        - Audit
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/audit/verify:
    get:
      operationId: get_v1_tenants_ByTenantID_audit_verify
      summary: Verify audit chain
      tags:
        - Audit
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/billing/invoices:
    get:
      operationId: get_v1_tenants_ByTenantID_billing_invoices
      summary: List invoices
      tags:
        - Billing
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/billing/subscription:
    get:
      operationId: get_v1_tenants_ByTenantID_billing_subscription
      summary: Get subscription
      tags:
        - Billing
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      operationId: put_v1_tenants_ByTenantID_billing_subscription
      summary: Subscribe / change plan
      tags:
        - Billing
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
        description: Request body summarized; see the handler for exact fields.
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/billing/subscription/cancel:
    post:
      operationId: post_v1_tenants_ByTenantID_billing_subscription_cancel
      summary: Cancel at period end
      tags:
        - Billing
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
        description: Request body summarized; see the handler for exact fields.
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/email-templates:
    get:
      operationId: get_v1_tenants_ByTenantID_email_templates
      summary: List templates
      tags:
        - Email Templates
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/email-templates/{key}:
    delete:
      operationId: delete_v1_tenants_ByTenantID_email_templates_ByKey
      summary: Reset to default
      tags:
        - Email Templates
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
        - name: key
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '204':
          description: No Content
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    get:
      operationId: get_v1_tenants_ByTenantID_email_templates_ByKey
      summary: Get template
      tags:
        - Email Templates
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
        - name: key
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      operationId: put_v1_tenants_ByTenantID_email_templates_ByKey
      summary: Override template
      tags:
        - Email Templates
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
        - name: key
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
        description: Request body summarized; see the handler for exact fields.
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/email-templates/{key}/preview:
    post:
      operationId: post_v1_tenants_ByTenantID_email_templates_ByKey_preview
      summary: Preview template
      tags:
        - Email Templates
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
        - name: key
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
        description: Request body summarized; see the handler for exact fields.
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/gdpr/purge:
    get:
      operationId: get_v1_tenants_ByTenantID_gdpr_purge
      summary: List purge requests
      tags:
        - GDPR
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/log-sinks:
    get:
      operationId: get_v1_tenants_ByTenantID_log_sinks
      summary: List log sinks
      tags:
        - Log Sinks
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      responses:
        '200':
          description: Configured sinks (tokens are write-only, not returned)
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      operationId: post_v1_tenants_ByTenantID_log_sinks
      summary: Create log sink
      tags:
        - Log Sinks
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - type
                - endpoint
              properties:
                type:
                  type: string
                  enum:
                    - splunk_hec
                    - datadog
                    - http
                endpoint:
                  type: string
                  format: uri
                token:
                  type: string
      responses:
        '201':
          description: Sink created (streams new audit events from now)
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/Unprocessable'
  /v1/tenants/{tenantID}/log-sinks/{id}:
    patch:
      operationId: patch_v1_tenants_ByTenantID_log_sinks_ById
      summary: Enable/disable sink
      tags:
        - Log Sinks
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
        - name: id
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
      responses:
        '200':
          description: Updated
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      operationId: delete_v1_tenants_ByTenantID_log_sinks_ById
      summary: Delete sink
      tags:
        - Log Sinks
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
        - name: id
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      responses:
        '204':
          description: Removed
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /v1/tenants/{tenantID}/retention:
    get:
      operationId: get_v1_tenants_ByTenantID_retention
      summary: Get retention policy
      tags:
        - Data Retention
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    put:
      operationId: put_v1_tenants_ByTenantID_retention
      summary: Update retention policy
      tags:
        - Data Retention
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
        description: Request body summarized; see the handler for exact fields.
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/retention/preview:
    post:
      operationId: post_v1_tenants_ByTenantID_retention_preview
      summary: Preview ripe users
      tags:
        - Data Retention
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
        description: Request body summarized; see the handler for exact fields.
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/tenants/{tenantID}/retention/run:
    post:
      operationId: post_v1_tenants_ByTenantID_retention_run
      summary: Run purge now
      tags:
        - Data Retention
      parameters:
        - name: tenantID
          in: path
          required: true
          schema:
            type: string
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
        description: Request body summarized; see the handler for exact fields.
      description: Response body summarized; accurate path/method/params/security. Enrich the body later.
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /v1/notifications:
    get:
      operationId: get_v1_notifications
      summary: List notifications
      tags:
        - Notifications
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      responses:
        '200':
          description: Notification inbox
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  unread_count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
  /v1/notifications/mark-all-read:
    post:
      operationId: post_v1_notifications_mark_all_read
      summary: Mark all read
      tags:
        - Notifications
      security:
        - bearerAuth: []
        - apiKeyAuth: []
      responses:
        '204':
          description: All notifications marked read
        '401':
          $ref: '#/components/responses/Unauthorized'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'User or service JWT: `Authorization: Bearer <token>`.'
    apiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: 'API key credential. Send `Authorization: ApiKey <key>` (note the `ApiKey ` prefix). Accepted on the same authenticated routes as bearerAuth.'
    scimBearer:
      type: http
      scheme: bearer
      description: 'Per-tenant SCIM provisioning token presented by the IdP: `Authorization: Bearer <scim-token>`. Distinct from user JWTs; rotated via the SCIM admin endpoints.'
    clientCredentials:
      type: oauth2
      description: OAuth 2.0 client credentials. Clients authenticate at the token endpoint via form params (`client_id`/`client_secret`) or HTTP Basic. Revocation/introspection use the same client auth.
      flows:
        clientCredentials:
          tokenUrl: /v1/oauth/token
          scopes: {}
    ssoCookie:
      type: apiKey
      in: cookie
      name: qe_ls
      description: Hosted-login SSO session cookie (HttpOnly). Set by POST /v1/auth/session and consumed by the OAuth authorize/consent flow.
  responses:
    Unauthorized:
      description: Missing or invalid credentials
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: Authenticated but not permitted
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unprocessable:
      description: Validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    TooManyRequests:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    RateLimitBucket:
      type: object
      description: A single token-bucket limit (rate tokens/sec, burst capacity).
      properties:
        rate:
          type: number
          description: Sustained tokens per second.
        capacity:
          type: integer
          description: Maximum burst capacity.
    TenantRateLimits:
      type: object
      description: Per-bucket rate limits for a tenant. On GET these are the effective values (defaults merged with overrides); on PUT, any bucket supplied is upserted as an override.
      properties:
        tenant:
          $ref: '#/components/schemas/RateLimitBucket'
        user:
          $ref: '#/components/schemas/RateLimitBucket'
        api_key:
          $ref: '#/components/schemas/RateLimitBucket'
    Error:
      type: object
      required:
        - error
      description: Canonical error envelope (internal/platform/httpx.WriteError).
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Stable machine code
              example: unauthorized
            message:
              type: string
              example: authentication required
            detail:
              type: string
              description: Optional human detail
            request_id:
              type: string
              description: Correlates with the X-Request-Id header
