openapi: 3.0.0
info:
  title: UnboundBytes API
  version: 1.0.0
  description: |-
    Multi-tenant control plane for self-hosted SaaS workloads.

    The UnboundBytes platform provides REST endpoints for device management,
    application deployment, backups, tunnels, and tenant operations.

    - **Portal API**: For web UI and third-party integrations (JWT auth)
    - **Orchestrator API**: For agent communication (HMAC auth)

    **API Base**: https://api.unboundbytes.com/api/v1

    ## API Versioning

    All API endpoints are versioned using URL path versioning. The current stable
    version is `v1`. All endpoints should be accessed via `/api/v1/...`.

    ### Deprecation Policy

    - Deprecated endpoints will be announced at least 6 months before removal
    - The `Deprecation` header indicates when deprecation was announced
    - The `Sunset` header indicates when the endpoint will be removed
    - Clients should migrate to newer versions before the sunset date
  contact:
    name: UnboundBytes Team
    url: https://unboundbytes.com
  license:
    name: Proprietary
    url: https://app.unboundbytes.com/legal/terms
servers:
  - url: https://api.unboundbytes.com
    description: Production API
  - url: https://api-staging.unboundbytes.com
    description: Staging API
tags:
  - name: Health & Status
    description: Health check and status endpoints
  - name: Pairing
    description: Device pairing and enrollment
  - name: Devices
    description: Device management operations
  - name: Apps
    description: Application management
  - name: Deployments
    description: Deployment operations
  - name: Backups
    description: Backup and restore operations
  - name: Tunnels
    description: Cloudflare tunnel management
  - name: Tenants
    description: Tenant management
  - name: Billing
    description: Billing and subscription management
  - name: Admin
    description: Administrative endpoints
  - name: Batch
    description: Batch operations
  - name: Alerts
    description: Alert management
  - name: Traces
    description: Distributed tracing
  - name: Internal
    description: Internal system endpoints
  - name: Fleet Management
    description: Fleet version management and update policies
  - name: Custom Domains
    description: Custom domain management
  - name: Roles
    description: Role-based access control
  - name: Invites
    description: Team invitation management
  - name: Bulk Operations
    description: Bulk app and device operations
  - name: Data Export
    description: Data export and import operations
  - name: Account
    description: Account management and GDPR compliance
  - name: Mesh Routes
    description: Mesh route provisioning
  - name: Webhooks
    description: Webhook subscription management
  - name: Cost
    description: Cost tracking and analysis
  - name: Volumes
    description: Volume management
  - name: Security Alerts
    description: Security alert management
  - name: Backup Policies
    description: Backup policy management
  - name: Agent
    description: Device agent communication endpoints
  - name: General
    description: General API endpoints (OIDC, changelog, info)
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key for portal authentication
    HmacAuth:
      type: apiKey
      in: header
      name: X-UB-Signature
      description: HMAC signature for agent authentication (includes X-UB-Timestamp header)
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT token from OIDC provider for portal users
  schemas:
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - false
          description: Always false for error responses
        error:
          type: string
          description: Human-readable error message
        code:
          type: string
          description: Machine-readable error code (e.g. VALIDATION_FAILED, UNAUTHORIZED, RATE_LIMIT_EXCEEDED)
        retryable:
          type: boolean
          description: Whether the client should retry this request
        timestamp:
          type: string
          format: date-time
          description: ISO 8601 timestamp when the error occurred
        requestId:
          type: string
          description: Unique request ID for tracing and debugging
        details:
          type: object
          additionalProperties: {}
          description: Additional error context (validation errors, limits, etc.)
        retryAfter:
          type: number
          description: Seconds to wait before retrying (for 429/503 responses)
      required:
        - success
        - error
        - code
        - retryable
        - timestamp
    Pagination:
      type: object
      properties:
        total:
          type: integer
          description: Total number of items
        limit:
          type: integer
          description: Maximum items per page
        offset:
          type: integer
          description: Number of items skipped
        hasMore:
          type: boolean
          description: Whether more items exist
      required:
        - total
        - limit
        - offset
        - hasMore
    HealthResponse:
      type: object
      properties:
        status:
          type: string
          enum:
            - healthy
            - degraded
            - unhealthy
          description: Service health status
        timestamp:
          type: string
          format: date-time
          description: Current server time
        version:
          type: string
          description: API version
      required:
        - status
        - timestamp
    Device:
      type: object
      properties:
        deviceId:
          type: string
          description: Device ID (dev_XXX)
        deploymentId:
          type: string
          description: Deployment this device belongs to
        agentId:
          type: string
          description: Agent ID running on this device
        fingerprint:
          type: string
          description: Device fingerprint
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp
        lastSeen:
          type: string
          format: date-time
          description: Last seen timestamp
        lastHeartbeatAt:
          type: string
          format: date-time
          description: Last heartbeat timestamp
        status:
          type: string
          enum:
            - online
            - offline
            - provisioning
          description: Device status
      required:
        - deviceId
        - deploymentId
        - agentId
        - fingerprint
        - createdAt
        - status
    DeviceSecretsResponse:
      type: object
      properties:
        deviceId:
          type: string
          description: Device ID
        secrets:
          type: object
          additionalProperties:
            type: string
          description: Key-value secret map (TENANT_ID, DEVICE_TOKEN, etc.)
        timestamp:
          type: string
          format: date-time
          description: Provisioning timestamp
      required:
        - deviceId
        - secrets
        - timestamp
    RotateSecretResponse:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - true
        message:
          type: string
          description: Rotation status message
      required:
        - success
        - message
    PaginationMeta:
      type: object
      properties:
        page:
          type: integer
          description: Current page number
        pageSize:
          type: integer
          description: Items per page
        total:
          type: integer
          description: Total number of items
        totalPages:
          type: integer
          description: Total number of pages
        hasNext:
          type: boolean
          description: Whether more pages exist
        hasPrev:
          type: boolean
          description: Whether previous pages exist
      required:
        - page
        - pageSize
        - total
        - totalPages
        - hasNext
        - hasPrev
    Tenant:
      type: object
      properties:
        tenantId:
          type: string
          description: Tenant ID (ten_XXX)
        slug:
          type: string
          description: URL-safe tenant identifier
        plan:
          type: string
          description: Billing plan (starter, pro, team, business)
        status:
          type: string
          description: Tenant status (active, suspended, etc.)
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp
        durableObjectId:
          type: string
          description: Durable Object backing ID
        deploymentCount:
          type: integer
          description: Number of deployments
        appCount:
          type: integer
          description: Number of apps
        tunnelCount:
          type: integer
          description: Number of tunnels
      required:
        - tenantId
        - slug
        - plan
        - status
        - createdAt
        - durableObjectId
        - deploymentCount
        - appCount
        - tunnelCount
    CreateTenantRequest:
      type: object
      properties:
        name:
          type: string
          description: Tenant display name
        slug:
          type: string
          description: URL-safe tenant identifier
        userId:
          type: string
          description: OIDC user ID (owner)
      required:
        - name
        - slug
        - userId
    CreateTenantResponse:
      type: object
      properties:
        tenantId:
          type: string
          description: Generated tenant ID
        durableObjectId:
          type: string
          description: Durable Object backing ID
        name:
          type: string
          description: Tenant display name
        slug:
          type: string
          description: URL-safe tenant identifier
        owner:
          type: string
          description: OIDC user ID of the owner
      required:
        - tenantId
        - durableObjectId
        - name
        - slug
        - owner
    UpdateTenantRequest:
      type: object
      properties:
        name:
          type: string
          description: Updated tenant name
        slug:
          type: string
          description: Updated slug
        plan:
          type: string
          description: Updated billing plan
    LoadTestResponse:
      type: object
      properties:
        status:
          type: string
          description: Load test status (not_started, running, completed)
        tenantId:
          type: string
          description: Tenant ID
        lastRun:
          type: string
          nullable: true
          format: date-time
          description: Last run timestamp
        results:
          type: object
          nullable: true
          additionalProperties: {}
          description: Load test results
        configuration:
          type: object
          properties:
            duration:
              type: number
              description: Test duration in seconds
            concurrency:
              type: number
              description: Concurrent connections
            endpoints:
              type: array
              items:
                type: string
              description: Endpoints to test
          required:
            - duration
            - concurrency
            - endpoints
          description: Load test configuration
      required:
        - status
    DashboardSummary:
      type: object
      properties:
        deployments:
          type: object
          properties:
            total:
              type: integer
            active:
              type: integer
            failed:
              type: integer
          required:
            - total
            - active
            - failed
          description: Deployment status counts
        apps:
          type: object
          properties:
            total:
              type: integer
            running:
              type: integer
            stopped:
              type: integer
          required:
            - total
            - running
            - stopped
          description: App status counts
        recentActivity:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Recent activity entries
    TenantMetricsResponse:
      type: object
      properties:
        metric:
          type: string
          description: Metric name (health, activity, resources)
        tenantId:
          type: string
          description: Tenant ID
        range:
          type: string
          description: Time range (e.g. 24h, 7d)
        buckets:
          type: array
          items:
            type: object
            properties:
              timestamp:
                type: string
                format: date-time
                description: Bucket timestamp
              count:
                type: number
                description: Metric value for this bucket
            required:
              - timestamp
              - count
          description: Time-series data buckets
        summary:
          type: object
          additionalProperties: {}
          description: Summary statistics
      required:
        - metric
        - tenantId
        - range
        - buckets
        - summary
    SecurityEvent:
      type: object
      properties:
        id:
          type: string
          description: Event ID
        type:
          type: string
          description: Event type
        severity:
          type: string
          description: Severity level
        timestamp:
          type: string
          format: date-time
          description: Event timestamp
        details:
          type: object
          additionalProperties: {}
          description: Event details
      required:
        - id
        - type
        - severity
        - timestamp
    SecurityAlert:
      type: object
      properties:
        id:
          type: string
          description: Alert ID
        type:
          type: string
          description: Alert type
        severity:
          type: string
          description: Severity level
        status:
          type: string
          enum:
            - open
            - acknowledged
            - resolved
          description: Alert status
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp
        acknowledgedAt:
          type: string
          format: date-time
          description: Acknowledged timestamp
        resolvedAt:
          type: string
          format: date-time
          description: Resolved timestamp
      required:
        - id
        - type
        - severity
        - status
        - createdAt
    SecurityMetrics:
      type: object
      properties:
        totalEvents:
          type: integer
          description: Total security events
        openAlerts:
          type: integer
          description: Open alert count
        blockedEntities:
          type: integer
          description: Blocked entity count
      required:
        - totalEvents
        - openAlerts
        - blockedEntities
    SecurityRule:
      type: object
      properties:
        id:
          type: string
          description: Rule ID
        name:
          type: string
          description: Rule name
        type:
          type: string
          description: Rule type
        enabled:
          type: boolean
          description: Whether rule is enabled
        conditions:
          type: object
          additionalProperties: {}
          description: Rule conditions
        actions:
          type: object
          additionalProperties: {}
          description: Rule actions
      required:
        - id
        - name
        - type
        - enabled
        - conditions
        - actions
    BlockedEntity:
      type: object
      properties:
        id:
          type: string
          description: Block entry ID
        entityType:
          type: string
          description: Entity type (ip, user, device)
        entityValue:
          type: string
          description: Blocked entity value
        reason:
          type: string
          description: Block reason
        createdAt:
          type: string
          format: date-time
          description: Block creation timestamp
      required:
        - id
        - entityType
        - entityValue
        - createdAt
    Deployment:
      type: object
      properties:
        deploymentId:
          type: string
          description: Deployment ID (dep_XXX)
        tenantId:
          type: string
          description: Owning tenant ID
        status:
          type: string
          description: Deployment status
        createdAt:
          type: string
          format: date-time
          description: Creation timestamp
        updatedAt:
          type: string
          format: date-time
          description: Last update timestamp
        name:
          type: string
          description: Deployment name
        runtime:
          type: string
          enum:
            - docker-compose
            - k3s
          description: Container runtime
      required:
        - deploymentId
        - tenantId
        - status
        - createdAt
        - updatedAt
    CreateDeploymentRequest:
      type: object
      properties:
        tenantId:
          type: string
          description: Tenant ID (auto-injected from auth)
        name:
          type: string
          description: Deployment name
        appName:
          type: string
          description: App catalog name to deploy
    UpdateDeploymentRequest:
      type: object
      properties:
        name:
          type: string
          description: Updated deployment name
        status:
          type: string
          description: Updated status
    DashboardMeta:
      type: object
      properties:
        deploymentId:
          type: string
          description: Deployment ID
        tenantId:
          type: string
          description: Tenant ID
        dashboardUid:
          type: string
          description: Grafana dashboard UID
        dashboardTitle:
          type: string
          description: Dashboard title
        dashboardTags:
          type: array
          items:
            type: string
          description: Dashboard tags
        timeRange:
          type: object
          additionalProperties: {}
          description: Default time range
        panelCount:
          type: integer
          description: Number of panels
        templateVariables:
          type: integer
          description: Number of template variables
      required:
        - deploymentId
        - tenantId
        - panelCount
        - templateVariables
    DashboardExport:
      type: object
      properties:
        dashboard:
          type: object
          additionalProperties: {}
          description: Full Grafana dashboard JSON
        exportedAt:
          type: string
          format: date-time
          description: Export timestamp
        deploymentId:
          type: string
          description: Deployment ID
      required:
        - dashboard
        - exportedAt
        - deploymentId
    VolumeInfo:
      type: object
      properties:
        name:
          type: string
          description: Volume name
        driver:
          type: string
          description: Volume driver
        mountpoint:
          type: string
          description: Host mount point
        size:
          type: number
          description: Volume size in bytes
        labels:
          type: object
          additionalProperties:
            type: string
          description: Volume labels
      required:
        - name
    VolumeDiscoveryResult:
      type: object
      properties:
        deviceId:
          type: string
          description: Device ID
        tenantId:
          type: string
          description: Tenant ID
        deploymentId:
          type: string
          description: Deployment ID
        volumes:
          type: array
          items:
            $ref: "#/components/schemas/VolumeInfo"
          description: Discovered volumes
        totalVolumes:
          type: integer
          description: Total volume count
        totalSize:
          type: number
          description: Total size in bytes
        discoveredAt:
          type: string
          format: date-time
          description: Discovery timestamp
      required:
        - deviceId
        - tenantId
        - deploymentId
        - volumes
        - totalVolumes
        - totalSize
    VolumeRefreshResponse:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - true
        message:
          type: string
          description: Status message
        deviceId:
          type: string
          description: Device ID
      required:
        - success
        - message
        - deviceId
    HealthCheckResult:
      type: object
      properties:
        name:
          type: string
          description: Check name
        status:
          type: string
          enum:
            - healthy
            - unhealthy
            - degraded
          description: Check status
        message:
          type: string
          description: Human-readable message
        durationMs:
          type: number
          description: Check duration in milliseconds
        timestamp:
          type: string
          format: date-time
          description: Check timestamp
        details:
          type: object
          additionalProperties: {}
          description: Additional details
      required:
        - name
        - status
        - durationMs
        - timestamp
    FullHealthResponse:
      type: object
      properties:
        overall:
          type: string
          enum:
            - healthy
            - unhealthy
            - degraded
          description: Overall health status
        checks:
          type: array
          items:
            $ref: "#/components/schemas/HealthCheckResult"
          description: Individual check results
        timestamp:
          type: string
          format: date-time
          description: Response timestamp
        uptime:
          type: number
          description: Uptime in milliseconds
        version:
          type: string
          description: API version
      required:
        - overall
        - checks
        - timestamp
        - uptime
        - version
    ReadinessResponse:
      type: object
      properties:
        ready:
          type: boolean
          description: Whether the service is ready
        checks:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              status:
                type: string
                enum:
                  - healthy
                  - unhealthy
                  - degraded
            required:
              - name
              - status
          description: Check results
        timestamp:
          type: string
          format: date-time
          description: Response timestamp
      required:
        - ready
        - checks
        - timestamp
    LivenessResponse:
      type: object
      properties:
        alive:
          type: boolean
          description: Whether the service is alive
        timestamp:
          type: string
          format: date-time
          description: Response timestamp
        uptime:
          type: number
          description: Uptime in milliseconds
      required:
        - alive
        - timestamp
        - uptime
    DeepHealthResponse:
      type: object
      properties:
        status:
          type: string
          enum:
            - healthy
            - degraded
            - unhealthy
          description: Overall deep health status
        dependencies:
          type: object
          properties:
            d1:
              type: object
              properties:
                status:
                  type: string
                  enum:
                    - healthy
                    - unhealthy
                    - degraded
                  description: Dependency health status
                latencyMs:
                  type: number
                  description: Check latency in ms
                configured:
                  type: boolean
                  description: Whether the dependency is configured
                message:
                  type: string
                  description: Human-readable status message
              required:
                - status
                - message
            r2:
              type: object
              properties:
                status:
                  type: string
                  enum:
                    - healthy
                    - unhealthy
                    - degraded
                  description: Dependency health status
                latencyMs:
                  type: number
                  description: Check latency in ms
                configured:
                  type: boolean
                  description: Whether the dependency is configured
                message:
                  type: string
                  description: Human-readable status message
              required:
                - status
                - message
            paddle:
              type: object
              properties:
                status:
                  type: string
                  enum:
                    - healthy
                    - unhealthy
                    - degraded
                  description: Dependency health status
                latencyMs:
                  type: number
                  description: Check latency in ms
                configured:
                  type: boolean
                  description: Whether the dependency is configured
                message:
                  type: string
                  description: Human-readable status message
              required:
                - status
                - message
            agentBinaries:
              type: object
              properties:
                status:
                  type: string
                  enum:
                    - healthy
                    - unhealthy
                    - degraded
                  description: Dependency health status
                latencyMs:
                  type: number
                  description: Check latency in ms
                configured:
                  type: boolean
                  description: Whether the dependency is configured
                message:
                  type: string
                  description: Human-readable status message
              required:
                - status
                - message
            sloTracking:
              type: object
              properties:
                status:
                  type: string
                  enum:
                    - healthy
                    - unhealthy
                    - degraded
                  description: Dependency health status
                latencyMs:
                  type: number
                  description: Check latency in ms
                configured:
                  type: boolean
                  description: Whether the dependency is configured
                message:
                  type: string
                  description: Human-readable status message
              required:
                - status
                - message
          required:
            - d1
            - r2
            - paddle
          description: Per-dependency health status
        timestamp:
          type: string
          format: date-time
          description: Response timestamp
        version:
          type: string
          description: API version
      required:
        - status
        - dependencies
        - timestamp
        - version
    PairingInitiateRequest:
      type: object
      properties:
        device_fingerprint:
          type: string
          description: Unique device fingerprint
        device_name:
          type: string
          description: Human-readable device name
        platform:
          type: string
          description: Device platform (linux, macos, windows)
      required:
        - device_fingerprint
    PairingInitiateResponse:
      type: object
      properties:
        pairing_code:
          type: string
          description: Short pairing code for user entry
        expires_at:
          type: string
          format: date-time
          description: Code expiration timestamp
        portal_url:
          type: string
          description: URL for user to complete pairing
      required:
        - pairing_code
        - expires_at
        - portal_url
    PrepareActivationRequest:
      type: object
      properties:
        pairing_code:
          type: string
          description: Pairing code to prepare
        tenant_id:
          type: string
          description: Tenant ID to activate under
      required:
        - pairing_code
        - tenant_id
    PrepareActivationResponse:
      type: object
      properties:
        activation_token:
          type: string
          description: Short-lived token for activation
        expires_at:
          type: string
          format: date-time
          description: Token expiration timestamp
      required:
        - activation_token
        - expires_at
    PairingActivateRequest:
      type: object
      properties:
        pairing_code:
          type: string
          description: Pairing code
        activation_token:
          type: string
          description: Activation token from prepare step
        user_email:
          type: string
          format: email
          description: User email for the device owner
        return_to:
          type: string
          description: URL to redirect after activation
      required:
        - pairing_code
        - activation_token
        - user_email
    PairingActivateResponse:
      type: object
      properties:
        device_id:
          type: string
          description: Newly created device ID
        agent_id:
          type: string
          description: Agent ID assigned to the device
        shared_secret:
          type: string
          description: Shared secret for HMAC auth
        tenant_id:
          type: string
          description: Tenant the device belongs to
        return_to:
          type: string
          description: Redirect URL
        infrastructure_secrets:
          type: object
          additionalProperties:
            type: string
          description: Infrastructure secrets for compose
      required:
        - device_id
        - agent_id
        - shared_secret
        - tenant_id
    PairingStatusResponse:
      type: object
      properties:
        status:
          type: string
          enum:
            - pending
            - activated
            - expired
            - not_found
          description: Pairing status
        tenant_id:
          type: string
          description: Tenant ID if activated
        agent_id:
          type: string
          description: Agent ID if activated
        device_id:
          type: string
          description: Device ID if activated
      required:
        - status
    AutoActivateRequest:
      type: object
      properties:
        enrollment_token:
          type: string
          description: Enrollment token from install script
        pairing_code:
          type: string
          description: Pairing code
        user_email:
          type: string
          format: email
          description: User email (auto-generated if omitted)
      required:
        - enrollment_token
        - pairing_code
    EnrollmentProgress:
      type: object
      properties:
        currentStep:
          type: string
          description: Current enrollment step
        steps:
          type: object
          additionalProperties: {}
          description: Step status map
        completedAt:
          type: string
          format: date-time
          description: Completion timestamp
        failedAt:
          type: string
          format: date-time
          description: Failure timestamp
        error:
          type: string
          description: Error message if failed
      required:
        - currentStep
        - steps
    EnrollmentProgressUpdateRequest:
      type: object
      properties:
        currentStep:
          type: string
          enum:
            - pending
            - preflight_check
            - docker_install
            - downloading
            - installing
            - enrolling
            - completed
            - failed
          description: Current enrollment step
        stepStatus:
          type: string
          enum:
            - pending
            - in_progress
            - completed
            - failed
            - skipped
          description: Step status
        error:
          type: string
          maxLength: 1000
          description: Error message if step failed
      required:
        - currentStep
    AppSummary:
      type: object
      properties:
        appName:
          type: string
          description: App name
        deploymentId:
          type: string
          description: Deployment ID
        status:
          type: string
          description: App status
        configuration:
          type: object
          additionalProperties: {}
          description: App configuration
      required:
        - appName
    AppSecrets:
      type: object
      additionalProperties:
        type: string
      description: Key-value secret map for the app
    AppTemplate:
      type: object
      properties:
        id:
          type: string
          description: Template identifier (e.g. 'immich', 'vaultwarden')
        name:
          type: string
          description: Display name
        description:
          type: string
          description: Template description
        categories:
          type: array
          items:
            type: string
          description: Template categories
        minPlan:
          type: string
          description: Minimum billing plan required
        available:
          type: boolean
          description: Whether the tenant's plan is sufficient
        requiredPlan:
          type: string
          description: Plan name shown when unavailable
      required:
        - id
        - name
        - description
        - categories
    App:
      type: object
      properties:
        id:
          type: string
          description: App instance ID
        tenantId:
          type: string
          description: Owning tenant ID
        deploymentId:
          type: string
          description: Associated deployment ID
        appType:
          type: string
          description: App template type
        status:
          type: string
          description: App status
        name:
          type: string
          description: App display name
        createdAt:
          type: string
          description: ISO 8601 creation timestamp
        updatedAt:
          type: string
          description: ISO 8601 last update timestamp
      required:
        - id
        - tenantId
        - appType
        - status
    AppSnapshot:
      type: object
      properties:
        snapshotId:
          type: string
          description: Snapshot identifier
        appId:
          type: string
          description: Parent app ID
        createdAt:
          type: string
          description: ISO 8601 snapshot creation timestamp
        metadata:
          type: object
          additionalProperties: {}
          description: Snapshot metadata
      required:
        - snapshotId
        - appId
        - createdAt
    CreateAppRequest:
      type: object
      properties:
        appType:
          type: string
          description: App template type (e.g. 'immich')
        name:
          type: string
          description: App display name
        tenantId:
          type: string
          description: Tenant ID (set automatically)
        deploymentId:
          type: string
          description: Target deployment ID
        config:
          type: object
          additionalProperties: {}
          description: App-specific configuration
      required:
        - appType
    UpdateAppRequest:
      type: object
      properties:
        name:
          type: string
          description: Updated display name
        config:
          type: object
          additionalProperties: {}
          description: Updated configuration
        status:
          type: string
          description: Updated status
    RollbackRequest:
      type: object
      properties:
        snapshotId:
          type: string
          description: Target snapshot ID to roll back to
      required:
        - snapshotId
    Tunnel:
      type: object
      properties:
        tunnelId:
          type: string
          description: Tunnel identifier
        deviceId:
          type: string
          description: Associated device ID
        status:
          type: string
          description: Tunnel status (active, stopped, etc.)
        createdAt:
          type: string
          description: ISO 8601 creation timestamp
        hostname:
          type: string
          description: Tunnel hostname
      required:
        - tunnelId
        - status
    TunnelCredentials:
      type: object
      properties:
        tunnelId:
          type: string
          description: Tunnel identifier
        token:
          type: string
          description: Cloudflare tunnel token
        accountTag:
          type: string
          description: Cloudflare account tag
      required:
        - tunnelId
        - token
    TunnelHealth:
      type: object
      properties:
        tunnelId:
          type: string
          description: Tunnel identifier
        healthy:
          type: boolean
          description: Whether the tunnel is healthy
        lastSeen:
          type: string
          description: Last heartbeat timestamp
        connections:
          type: number
          description: Active connections count
      required:
        - tunnelId
        - healthy
    CreateDeviceTunnelRequest:
      type: object
      properties:
        hostname:
          type: string
          description: Desired tunnel hostname
        service:
          type: string
          description: Backend service URL
    CreateGlobalTunnelRequest:
      type: object
      properties:
        name:
          type: string
          description: Tunnel name
        config:
          type: object
          additionalProperties: {}
          description: Tunnel configuration
    UpdateTunnelRequest:
      type: object
      properties:
        hostname:
          type: string
          description: Updated hostname
        service:
          type: string
          description: Updated backend service URL
        status:
          type: string
          description: Updated status
    BillingPlan:
      type: object
      properties:
        tier:
          type: string
          description: Plan tier identifier (free, starter, pro, team, business)
        name:
          type: string
          description: Plan display name
        limits:
          type: object
          additionalProperties: {}
          description: Resource limits
        features:
          type: array
          items:
            type: string
          description: Included features
        monthlyPrice:
          type: number
          description: Monthly price in cents
        annualPrice:
          type: number
          description: Annual price per month equivalent
      required:
        - tier
        - name
        - limits
        - features
        - monthlyPrice
        - annualPrice
    BillingPlansResponse:
      type: object
      properties:
        plans:
          type: array
          items:
            $ref: "#/components/schemas/BillingPlan"
          description: Available plans
        defaultPlan:
          type: string
          description: Default plan tier
        trialDays:
          type: number
          description: Trial period in days
      required:
        - plans
        - defaultPlan
        - trialDays
    TenantBilling:
      type: object
      properties:
        billing:
          type: object
          properties:
            plan:
              type: string
              description: Current plan tier
            status:
              type: string
              description: Subscription status
            entitlements:
              type: object
              additionalProperties: {}
              description: Resource entitlements
            updatedAt:
              type: string
              description: Last update timestamp
            cancelAtPeriodEnd:
              type: boolean
              description: Whether cancellation is pending
            cancellationEffectiveDate:
              type: string
              description: When cancellation takes effect
          required:
            - plan
            - status
          description: Billing information
        planDetails:
          allOf:
            - $ref: "#/components/schemas/BillingPlan"
            - description: Current plan details
        usage:
          type: object
          nullable: true
          properties:
            deploymentCount:
              type: number
              description: Deployment count
            hostCount:
              type: number
              description: Device count
            appCount:
              type: number
              description: App count
            storageGB:
              type: number
              description: Storage used in GB
            customDomainCount:
              type: number
              description: Custom domains
            lastUpdated:
              type: string
              description: Last usage update
          required:
            - deploymentCount
            - hostCount
            - appCount
            - storageGB
            - customDomainCount
          description: Current resource usage
      required:
        - billing
    CheckoutRequest:
      type: object
      properties:
        plan:
          type: string
          description: Target plan tier
        interval:
          type: string
          description: Billing interval (monthly, annual)
        email:
          type: string
          description: Customer email
        successUrl:
          type: string
          description: Redirect URL on success
        cancelUrl:
          type: string
          description: Redirect URL on cancel
      required:
        - plan
    CheckoutResponse:
      type: object
      properties:
        checkoutUrl:
          type: string
          description: Paddle checkout URL
        transactionId:
          type: string
          description: Paddle transaction ID
      required:
        - checkoutUrl
    CancelSubscriptionRequest:
      type: object
      properties:
        effectiveFrom:
          type: string
          description: When cancellation takes effect
        reason:
          type: string
          description: Cancellation reason
        feedback:
          type: string
          description: User feedback
    ChangePlanRequest:
      type: object
      properties:
        plan:
          type: string
          description: Target plan tier
        interval:
          type: string
          description: Billing interval
        prorate:
          type: boolean
          description: Whether to prorate
      required:
        - plan
    UpdateTenantPlanRequest:
      type: object
      properties:
        plan:
          type: string
          description: Target plan tier
        status:
          type: string
          description: Billing status override
        reason:
          type: string
          description: Reason for admin update
      required:
        - plan
    WebhookHealth:
      type: object
      properties:
        status:
          type: string
          description: Webhook configuration health status
        lastReceived:
          type: string
          description: Last webhook received timestamp
        details:
          type: object
          additionalProperties: {}
          description: Health check details
      required:
        - status
    BatchOperation:
      type: object
      properties:
        operationId:
          type: string
          description: Operation identifier
        type:
          type: string
          description: Operation type (deploy, restart, etc.)
        status:
          type: string
          description: Operation status
        progress:
          type: number
          description: Progress percentage
        createdAt:
          type: string
          description: ISO 8601 creation timestamp
        completedAt:
          type: string
          description: ISO 8601 completion timestamp
        results:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Per-target results
      required:
        - operationId
        - type
        - status
    CreateBatchOperationRequest:
      type: object
      properties:
        type:
          type: string
          description: Operation type
        targets:
          type: array
          items:
            type: string
          description: Target device/app IDs
        params:
          type: object
          additionalProperties: {}
          description: Operation parameters
      required:
        - type
        - targets
    AlertRule:
      type: object
      properties:
        ruleId:
          type: string
          description: Alert rule ID
        name:
          type: string
          description: Rule name
        condition:
          type: string
          description: Alert condition expression
        severity:
          type: string
          description: Alert severity
        enabled:
          type: boolean
          description: Whether the rule is enabled
        createdAt:
          type: string
          description: ISO 8601 creation timestamp
    AlertStats:
      type: object
      properties:
        total:
          type: number
          description: Total alerts
        critical:
          type: number
          description: Critical alerts count
        warning:
          type: number
          description: Warning alerts count
        info:
          type: number
          description: Info alerts count
    NotificationChannel:
      type: object
      properties:
        channelId:
          type: string
          description: Channel identifier
        type:
          type: string
          description: Channel type (email, webhook, slack)
        config:
          type: object
          additionalProperties: {}
          description: Channel configuration
        enabled:
          type: boolean
          description: Whether the channel is enabled
      required:
        - type
    CreateAlertRuleRequest:
      type: object
      properties:
        name:
          type: string
          description: Rule name
        condition:
          type: string
          description: Alert condition expression
        severity:
          type: string
          description: Alert severity
        channels:
          type: array
          items:
            type: string
          description: Notification channel IDs
      required:
        - name
        - condition
    CreateNotificationChannelRequest:
      type: object
      properties:
        type:
          type: string
          description: Channel type
        config:
          type: object
          additionalProperties: {}
          description: Channel configuration
      required:
        - type
        - config
    SecurityBlocked:
      type: object
      properties:
        blockId:
          type: string
          description: Block entry ID
        entity:
          type: string
          description: Blocked entity (IP, user, etc.)
        reason:
          type: string
          description: Block reason
        createdAt:
          type: string
          description: When the block was created
    CreateSecurityRuleRequest:
      type: object
      properties:
        name:
          type: string
          description: Rule name
        type:
          type: string
          description: Rule type
        config:
          type: object
          additionalProperties: {}
          description: Rule configuration
      required:
        - name
        - type
    UpdateSecurityRuleRequest:
      type: object
      properties:
        name:
          type: string
          description: Updated name
        config:
          type: object
          additionalProperties: {}
          description: Updated configuration
        enabled:
          type: boolean
          description: Updated enabled state
    CreateSecurityBlockRequest:
      type: object
      properties:
        entity:
          type: string
          description: Entity to block
        reason:
          type: string
          description: Block reason
      required:
        - entity
    Backup:
      type: object
      properties:
        backupId:
          type: string
          description: Backup identifier
        deviceId:
          type: string
          description: Source device ID
        appId:
          type: string
          description: Source app ID
        status:
          type: string
          description: Backup status
        sizeBytes:
          type: number
          description: Backup size in bytes
        createdAt:
          type: string
          description: ISO 8601 creation timestamp
        verified:
          type: boolean
          description: Whether backup integrity is verified
      required:
        - backupId
        - status
    BackupPolicy:
      type: object
      properties:
        policyId:
          type: string
          description: Policy identifier
        schedule:
          type: string
          description: Cron schedule expression
        retention:
          type: number
          description: Retention period in days
        targets:
          type: array
          items:
            type: string
          description: Target device/app IDs
        enabled:
          type: boolean
          description: Whether the policy is enabled
    CreateBackupPolicyRequest:
      type: object
      properties:
        schedule:
          type: string
          description: Cron schedule expression
        retention:
          type: number
          description: Retention period in days
        targets:
          type: array
          items:
            type: string
          description: Target device/app IDs
      required:
        - schedule
    BackupDrill:
      type: object
      properties:
        drillId:
          type: string
          description: Drill identifier
        status:
          type: string
          description: Drill status
        startedAt:
          type: string
          description: Drill start timestamp
        completedAt:
          type: string
          description: Drill completion timestamp
        results:
          type: object
          additionalProperties: {}
          description: Drill results
      required:
        - drillId
        - status
    BackupDrillConfig:
      type: object
      properties:
        enabled:
          type: boolean
          description: Whether automatic drills are enabled
        schedule:
          type: string
          description: Cron schedule for automatic drills
        retentionDays:
          type: number
          description: Drill results retention
      required:
        - enabled
    BackupVerificationSummary:
      type: object
      properties:
        totalBackups:
          type: number
          description: Total backup count
        verifiedCount:
          type: number
          description: Verified backups count
        failedCount:
          type: number
          description: Failed verification count
        lastVerifiedAt:
          type: string
          description: Last verification timestamp
      required:
        - totalBackups
        - verifiedCount
        - failedCount
    BackupMetrics:
      type: object
      properties:
        totalSizeBytes:
          type: number
          description: Total backup storage size
        backupCount:
          type: number
          description: Total backup count
        avgSizeBytes:
          type: number
          description: Average backup size
        successRate:
          type: number
          description: Backup success rate percentage
      required:
        - totalSizeBytes
        - backupCount
    BackupMonitoring:
      type: object
      properties:
        health:
          type: object
          additionalProperties: {}
          description: Health status
        metrics:
          type: object
          additionalProperties: {}
          description: Backup metrics
        alerts:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Active backup alerts
    RestoreJob:
      type: object
      properties:
        jobId:
          type: string
          description: Restore job identifier
        backupId:
          type: string
          description: Source backup ID
        status:
          type: string
          description: Job status
        startedAt:
          type: string
          description: Job start timestamp
        completedAt:
          type: string
          description: Job completion timestamp
      required:
        - jobId
        - backupId
        - status
    WebhookSubscription:
      type: object
      properties:
        webhookId:
          type: string
          description: Webhook identifier
        url:
          type: string
          description: Webhook delivery URL
        events:
          type: array
          items:
            type: string
          description: Subscribed event types
        description:
          type: string
          description: Webhook description
        active:
          type: boolean
          description: Whether the webhook is active
        createdAt:
          type: string
          description: ISO 8601 creation timestamp
      required:
        - url
        - events
    CreateWebhookRequest:
      type: object
      properties:
        url:
          type: string
          description: Webhook delivery URL
        events:
          type: array
          items:
            type: string
          minItems: 1
          description: Event types to subscribe to
        description:
          type: string
          description: Webhook description
        secret:
          type: string
          description: Signing secret for verification
        retryConfig:
          type: object
          properties:
            maxRetries:
              type: number
              description: Maximum retry attempts
            backoffMultiplier:
              type: number
              description: Backoff multiplier
            initialDelayMs:
              type: number
              description: Initial retry delay in ms
          description: Retry configuration
      required:
        - url
        - events
    UpdateWebhookRequest:
      type: object
      properties:
        url:
          type: string
          description: Updated delivery URL
        events:
          type: array
          items:
            type: string
          description: Updated event types
        description:
          type: string
          description: Updated description
        active:
          type: boolean
          description: Updated active state
        retryConfig:
          type: object
          properties:
            maxRetries:
              type: number
              description: Maximum retry attempts
            backoffMultiplier:
              type: number
              description: Backoff multiplier
            initialDelayMs:
              type: number
              description: Initial retry delay in ms
          description: Updated retry configuration
    WebhookDelivery:
      type: object
      properties:
        deliveryId:
          type: string
          description: Delivery identifier
        webhookId:
          type: string
          description: Parent webhook ID
        event:
          type: string
          description: Event type
        statusCode:
          type: number
          description: HTTP response status code
        success:
          type: boolean
          description: Whether delivery succeeded
        attemptCount:
          type: number
          description: Number of delivery attempts
        deliveredAt:
          type: string
          description: Delivery timestamp
    WebhookStats:
      type: object
      properties:
        totalDeliveries:
          type: number
          description: Total delivery attempts
        successCount:
          type: number
          description: Successful deliveries
        failureCount:
          type: number
          description: Failed deliveries
        successRate:
          type: number
          description: Success rate percentage
        avgResponseTimeMs:
          type: number
          description: Average response time
    WebhookTestResult:
      type: object
      properties:
        success:
          type: boolean
          description: Whether test delivery succeeded
        statusCode:
          type: number
          description: HTTP response status code
        responseTimeMs:
          type: number
          description: Response time in ms
        error:
          type: string
          description: Error message if failed
      required:
        - success
    Command:
      type: object
      properties:
        commandId:
          type: string
          description: Command identifier
        type:
          type: string
          description: Command type (deploy, restart, etc.)
        payload:
          type: object
          additionalProperties: {}
          description: Command payload
        status:
          type: string
          description: Command status
        createdAt:
          type: string
          description: ISO 8601 creation timestamp
      required:
        - commandId
        - type
    CommandList:
      type: object
      properties:
        commands:
          type: array
          items:
            $ref: "#/components/schemas/Command"
          description: Pending commands
      required:
        - commands
    PostCommandRequest:
      type: object
      properties:
        type:
          type: string
          description: Command type
        payload:
          type: object
          additionalProperties: {}
          description: Command payload
        priority:
          type: number
          description: Command priority
      required:
        - type
    CommandAckRequest:
      type: object
      properties:
        status:
          type: string
          description: Execution status (success, failed)
        output:
          type: string
          description: Command output
        error:
          type: string
          description: Error message if failed
      required:
        - status
    HeartbeatRequest:
      type: object
      properties:
        status:
          type: string
          description: Agent status (online, offline, error)
        lastSeen:
          type: string
          description: ISO 8601 timestamp
        agentVersion:
          type: string
          description: Agent semver version
        metrics:
          type: object
          additionalProperties: {}
          description: System metrics
        apps:
          type: array
          items:
            type: object
            properties:
              appId:
                type: string
              status:
                type: string
                enum:
                  - running
                  - stopped
                  - error
              uptime:
                type: number
            required:
              - appId
              - status
          description: Running apps status
    HeartbeatResponse:
      type: object
      properties:
        acknowledged:
          type: boolean
          description: Whether heartbeat was acknowledged
        serverTime:
          type: string
          description: Server timestamp
      required:
        - acknowledged
    PublicKey:
      type: object
      properties:
        deviceId:
          type: string
          description: Device ID
        publicKey:
          type: object
          properties:
            kty:
              type: string
              description: Key type
            crv:
              type: string
              description: Curve
            x:
              type: string
              description: X coordinate
            y:
              type: string
              description: Y coordinate
          required:
            - kty
            - crv
            - x
            - y
          description: JWK public key
      required:
        - deviceId
        - publicKey
    LogForwardRequest:
      type: object
      properties:
        logs:
          type: array
          items:
            type: object
            properties:
              level:
                type: string
                enum:
                  - debug
                  - info
                  - warn
                  - error
                description: Log level
              message:
                type: string
                description: Log message
              timestamp:
                type: string
                description: Log timestamp
              source:
                type: string
                description: Log source
              details:
                type: object
                additionalProperties: {}
                description: Additional details
            required:
              - level
              - message
          minItems: 1
          maxItems: 100
          description: Batched log entries
      required:
        - logs
    LogForwardResponse:
      type: object
      properties:
        accepted:
          type: number
          description: Number of log entries accepted
        dropped:
          type: number
          description: Number of entries dropped
      required:
        - accepted
    CostSummary:
      type: object
      properties:
        period:
          type: string
          description: Period (e.g. '30d')
        costs:
          type: object
          additionalProperties: {}
          description: Cost breakdown by service
        d1Metrics:
          type: object
          additionalProperties: {}
          description: D1 usage metrics
        r2Metrics:
          type: object
          additionalProperties: {}
          description: R2 usage metrics
      required:
        - period
        - costs
    TenantCost:
      type: object
      properties:
        tenantId:
          type: string
          description: Tenant identifier
        period:
          type: string
          description: Reporting period
        costs:
          type: object
          additionalProperties: {}
          description: Cost breakdown
        usage:
          type: object
          additionalProperties: {}
          description: Usage metrics
      required:
        - costs
    CostForecast:
      type: object
      properties:
        forecast:
          type: object
          additionalProperties: {}
          description: Forecasted costs
        confidence:
          type: number
          description: Forecast confidence (0-1)
        period:
          type: string
          description: Forecast period
      required:
        - forecast
    ExpensiveQuery:
      type: object
      properties:
        query:
          type: string
          description: SQL query text
        avgDurationMs:
          type: number
          description: Average duration in ms
        callCount:
          type: number
          description: Total call count
        totalCost:
          type: number
          description: Estimated total cost
    LifecycleStatus:
      type: object
      properties:
        compliant:
          type: boolean
          description: Whether lifecycle policies are compliant
        policies:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Active lifecycle policies
        violations:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Policy violations
      required:
        - compliant
    CostStatus:
      type: object
      properties:
        tenantId:
          type: string
          description: Tenant identifier
        period:
          type: string
          description: Reporting period
        costs:
          type: object
          additionalProperties: {}
          description: Cost breakdown
        usage:
          type: object
          additionalProperties: {}
          description: Usage metrics
        budget:
          type: object
          additionalProperties: {}
          description: Budget status
        timestamp:
          type: string
          description: Snapshot timestamp
      required:
        - tenantId
        - costs
    BetaSignupRequest:
      type: object
      properties:
        email:
          type: string
          description: Email address
        name:
          type: string
          description: Full name
        source:
          type: string
          description: Signup source
        metadata:
          type: string
          description: Additional metadata
      required:
        - email
    BetaSignupResponse:
      type: object
      properties:
        message:
          type: string
          description: Result message
      required:
        - message
    HealthAlert:
      type: object
      properties:
        alertId:
          type: string
          description: Alert identifier
        severity:
          type: string
          description: Alert severity
        message:
          type: string
          description: Alert message
        component:
          type: string
          description: Affected component
        timestamp:
          type: string
          description: Alert timestamp
    DbStats:
      type: object
      properties:
        tables:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: Table statistics
        totalRows:
          type: number
          description: Total row count
        totalSizeBytes:
          type: number
          description: Total database size
    UpdatePolicy:
      type: object
      properties:
        deviceId:
          type: string
          description: Device ID
        policy:
          type: string
          enum:
            - auto
            - pinned
            - rollback
          description: Update policy type
        pinnedVersion:
          type: string
          nullable: true
          description: Pinned version (semver) when policy is 'pinned'
        rollbackToVersion:
          type: string
          nullable: true
          description: Target rollback version when policy is 'rollback'
        updatedAt:
          type: string
          nullable: true
          description: ISO 8601 timestamp of last update
      required:
        - deviceId
        - policy
        - pinnedVersion
        - rollbackToVersion
        - updatedAt
    SetUpdatePolicyRequest:
      type: object
      properties:
        policy:
          type: string
          enum:
            - auto
            - pinned
            - rollback
          description: Update policy type
        pinnedVersion:
          type: string
          description: Required when policy is 'pinned' — semver format
        rollbackToVersion:
          type: string
          description: Required when policy is 'rollback' — semver format
      required:
        - policy
    RollbackAgentsRequest:
      type: object
      properties:
        rollbackToVersion:
          type: string
          description: Target rollback version (semver format)
      required:
        - rollbackToVersion
    RollbackAgentsResponse:
      type: object
      properties:
        deploymentId:
          type: string
          description: Deployment ID
        rollbackToVersion:
          type: string
          description: Target rollback version
        devicesAffected:
          type: integer
          description: Number of devices set to rollback
        deviceIds:
          type: array
          items:
            type: string
          description: List of affected device IDs
      required:
        - deploymentId
        - rollbackToVersion
        - devicesAffected
        - deviceIds
    UpdateCheckResponse:
      type: object
      properties:
        policy:
          type: string
          enum:
            - auto
            - pinned
            - rollback
          description: Current update policy
        pinnedVersion:
          type: string
          nullable: true
          description: Pinned version if applicable
        rollbackToVersion:
          type: string
          nullable: true
          description: Rollback target version if applicable
        rollbackRequested:
          type: boolean
          description: Whether a rollback is currently requested
      required:
        - policy
        - pinnedVersion
        - rollbackToVersion
        - rollbackRequested
    CustomDomain:
      type: object
      properties:
        id:
          type: string
          description: Custom domain ID
        domain:
          type: string
          description: Domain name
        appName:
          type: string
          description: Associated app name
        status:
          type: string
          description: Domain status (pending_dns, pending_ssl, active, error)
        sslStatus:
          type: string
          nullable: true
          description: SSL certificate status
        cfHostnameId:
          type: string
          nullable: true
          description: Cloudflare custom hostname ID
        cfHostnameStatus:
          type: string
          description: Cloudflare hostname status
        verifiedAt:
          type: string
          nullable: true
          description: ISO 8601 verification timestamp
        cnameTarget:
          type: string
          description: CNAME target for DNS setup
      required:
        - id
        - domain
        - appName
        - status
    AddDomainRequest:
      type: object
      properties:
        domain:
          type: string
          minLength: 1
          maxLength: 253
          description: Domain name to add
        appName:
          type: string
          minLength: 1
          maxLength: 63
          description: App name to associate with the domain
      required:
        - domain
        - appName
    AddDomainResponse:
      type: object
      properties:
        id:
          type: string
          description: Custom domain ID
        domain:
          type: string
          description: Domain name
        cnameTarget:
          type: string
          description: CNAME target for DNS setup
        status:
          type: string
          enum:
            - pending_dns
          description: Initial status
      required:
        - id
        - domain
        - cnameTarget
        - status
    VerifyDomainResponse:
      type: object
      properties:
        id:
          type: string
          description: Custom domain ID
        domain:
          type: string
          description: Domain name
        status:
          type: string
          enum:
            - pending_ssl
          description: Status after verification
        cfHostnameId:
          type: string
          description: Cloudflare custom hostname ID
        sslStatus:
          type: string
          description: SSL certificate status
      required:
        - id
        - domain
        - status
        - cfHostnameId
        - sslStatus
    RoleAssignment:
      type: object
      properties:
        userId:
          type: string
          description: User ID
        role:
          type: string
          enum:
            - owner
            - admin
            - member
          description: Assigned role
        createdAt:
          type: number
          description: Unix timestamp of role creation
        updatedAt:
          type: number
          description: Unix timestamp of last update
      required:
        - userId
        - role
    SetRoleRequest:
      type: object
      properties:
        role:
          type: string
          enum:
            - owner
            - admin
            - member
          description: Role to assign
      required:
        - role
    SetRoleResponse:
      type: object
      properties:
        userId:
          type: string
          description: Target user ID
        role:
          type: string
          enum:
            - owner
            - admin
            - member
          description: Assigned role
        updatedAt:
          type: number
          description: Unix timestamp of update
      required:
        - userId
        - role
        - updatedAt
    MyRoleResponse:
      type: object
      properties:
        userId:
          type: string
          nullable: true
          description: User ID (null for API key auth)
        role:
          type: string
          enum:
            - owner
            - admin
            - member
          description: Current role
      required:
        - userId
        - role
    Invite:
      type: object
      properties:
        inviteId:
          type: string
          description: Invite ID
        tenantId:
          type: string
          description: Tenant ID
        email:
          type: string
          description: Invited email address
        role:
          type: string
          enum:
            - owner
            - admin
            - member
          description: Role assigned to invitee
        status:
          type: string
          enum:
            - pending
            - accepted
            - revoked
            - expired
          description: Invite status
        invitedBy:
          type: string
          nullable: true
          description: User who created the invite
        expiresAt:
          type: string
          description: ISO 8601 expiration timestamp
        acceptedAt:
          type: string
          nullable: true
          description: ISO 8601 acceptance timestamp
        createdAt:
          type: string
          description: ISO 8601 creation timestamp
      required:
        - inviteId
        - tenantId
        - email
        - role
        - status
        - invitedBy
        - expiresAt
        - acceptedAt
        - createdAt
    CreateInviteRequest:
      type: object
      properties:
        email:
          type: string
          maxLength: 255
          format: email
          description: Email address to invite
        role:
          type: string
          enum:
            - owner
            - admin
            - member
          default: member
          description: Role to assign
        invitedBy:
          type: string
          description: Name of the person sending the invite
      required:
        - email
    CreateInviteResponse:
      type: object
      properties:
        inviteId:
          type: string
          description: Invite ID
        inviteCode:
          type: string
          description: Invite code for the invite link
        email:
          type: string
          description: Invited email address
        role:
          type: string
          enum:
            - owner
            - admin
            - member
          description: Assigned role
        expiresAt:
          type: string
          description: ISO 8601 expiration timestamp
      required:
        - inviteId
        - inviteCode
        - email
        - role
        - expiresAt
    ValidateInviteResponse:
      type: object
      properties:
        email:
          type: string
          description: Invited email address
        role:
          type: string
          description: Assigned role
        tenantName:
          type: string
          nullable: true
          description: Tenant name
        tenantSlug:
          type: string
          nullable: true
          description: Tenant slug
        expiresAt:
          type: string
          description: ISO 8601 expiration timestamp
      required:
        - email
        - role
        - tenantName
        - tenantSlug
        - expiresAt
    AcceptInviteRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Display name for new account
        password:
          type: string
          minLength: 12
          maxLength: 128
          description: Password for new account
        clientIp:
          type: string
          maxLength: 45
          description: Client IP address
    AcceptInviteResponse:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - true
        userId:
          type: string
          description: User ID
        tenantId:
          type: string
          description: Tenant ID
        alreadyMember:
          type: boolean
          description: True if user was already a member
      required:
        - success
        - userId
        - tenantId
    BulkOperationResult:
      type: object
      properties:
        operationId:
          type: string
          description: Unique bulk operation ID
        status:
          type: string
          enum:
            - completed
            - partial
            - failed
          description: Overall operation status
        total:
          type: integer
          description: Total number of items
        succeeded:
          type: integer
          description: Number of successful items
        failed:
          type: integer
          description: Number of failed items
        pending:
          type: integer
          description: Number of pending items
        results:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                description: Item identifier
              status:
                type: string
                enum:
                  - success
                  - error
                  - pending
                  - skipped
                description: Item status
              result:
                type: object
                additionalProperties: {}
                description: Item result data
              error:
                type: string
                description: Error message if failed
              timestamp:
                type: string
                description: ISO 8601 timestamp
            required:
              - id
              - status
          description: Per-item results
        createdAt:
          type: string
          description: Operation start timestamp
        updatedAt:
          type: string
          description: Last update timestamp
        completedAt:
          type: string
          description: Completion timestamp
      required:
        - operationId
        - status
        - total
        - succeeded
        - failed
        - pending
        - results
        - createdAt
        - updatedAt
    BulkAppCreateRequest:
      type: object
      properties:
        apps:
          type: array
          items:
            type: object
            properties:
              appType:
                type: string
                description: Application type
              name:
                type: string
                description: Application name
              deploymentId:
                type: string
                description: Target deployment ID
              config:
                type: object
                additionalProperties:
                  type: string
                description: App configuration
            required:
              - appType
              - name
              - deploymentId
          minItems: 1
          maxItems: 100
          description: Apps to create
        options:
          type: object
          properties:
            continueOnError:
              type: boolean
              description: Continue if individual items fail
            maxConcurrency:
              type: integer
              description: Max parallel operations
            transactional:
              type: boolean
              description: Rollback all on failure
          description: Bulk operation options
      required:
        - apps
    BulkAppUpdateRequest:
      type: object
      properties:
        updates:
          type: array
          items:
            type: object
            properties:
              appId:
                type: string
                description: App ID to update
              changes:
                type: object
                additionalProperties: {}
                description: Fields to update
            required:
              - appId
              - changes
          minItems: 1
          maxItems: 100
          description: App updates
        options:
          type: object
          properties:
            continueOnError:
              type: boolean
              description: Continue if individual items fail
            maxConcurrency:
              type: integer
              description: Max parallel operations
          description: Bulk operation options
      required:
        - updates
    BulkAppDeleteRequest:
      type: object
      properties:
        appIds:
          type: array
          items:
            type: string
          minItems: 1
          maxItems: 100
          description: App IDs to delete
        options:
          type: object
          properties:
            continueOnError:
              type: boolean
              description: Continue if individual items fail
            maxConcurrency:
              type: integer
              description: Max parallel operations
          description: Bulk operation options
      required:
        - appIds
    BulkDeviceOperationRequest:
      type: object
      properties:
        deviceIds:
          type: array
          items:
            type: string
          minItems: 1
          maxItems: 100
          description: Device IDs to operate on
        operation:
          type: object
          properties:
            type:
              type: string
              enum:
                - restart
                - update_config
                - rotate_secrets
                - health_check
                - deploy_app
                - remove_app
              description: Operation type
            payload:
              type: object
              additionalProperties: {}
              description: Operation-specific payload
          required:
            - type
          description: Operation to perform
        options:
          type: object
          properties:
            continueOnError:
              type: boolean
              description: Continue if individual items fail
            maxConcurrency:
              type: integer
              description: Max parallel operations
            timeout:
              type: integer
              description: Per-item timeout in ms
          description: Bulk operation options
      required:
        - deviceIds
        - operation
    ExportJob:
      type: object
      properties:
        jobId:
          type: string
          description: Export job ID
        status:
          type: string
          description: Job status (pending, processing, completed, failed)
        exportType:
          type: string
          description: Export type (full, deployments, devices, apps, etc.)
        format:
          type: string
          description: Export format (json, csv, yaml)
        createdAt:
          type: string
          description: ISO 8601 creation timestamp
      required:
        - jobId
        - status
        - exportType
        - createdAt
    CreateExportRequest:
      type: object
      properties:
        exportType:
          type: string
          enum:
            - full
            - deployments
            - devices
            - apps
            - backups
            - audit_logs
            - configuration
          description: Type of data to export
        format:
          type: string
          enum:
            - json
            - csv
            - yaml
          description: Export format
        filters:
          type: object
          properties:
            deploymentIds:
              type: array
              items:
                type: string
            deviceIds:
              type: array
              items:
                type: string
            appNames:
              type: array
              items:
                type: string
            dateFrom:
              type: string
            dateTo:
              type: string
            includeArchived:
              type: boolean
          description: Filters to apply to the export
        includeSensitive:
          type: boolean
          description: Include sensitive data in export
        encrypt:
          type: boolean
          description: Encrypt the export file
      required:
        - exportType
    ImportJob:
      type: object
      properties:
        jobId:
          type: string
          description: Import job ID
        status:
          type: string
          description: Job status (pending, processing, completed, failed)
        importType:
          type: string
          description: Import type (full, deployments, devices, apps, configuration)
        dryRun:
          type: boolean
          description: Whether this is a dry run
        createdAt:
          type: string
          description: ISO 8601 creation timestamp
      required:
        - jobId
        - status
        - importType
        - createdAt
    CreateImportRequest:
      type: object
      properties:
        importType:
          type: string
          enum:
            - full
            - deployments
            - devices
            - apps
            - configuration
          description: Type of data to import
        r2Key:
          type: string
          minLength: 1
          description: R2 key of the uploaded import file
        conflictStrategy:
          type: string
          enum:
            - skip
            - overwrite
            - merge
            - fail
          description: How to handle conflicts
        dryRun:
          type: boolean
          description: Validate without applying changes
      required:
        - importType
        - r2Key
    UploadImportFileResponse:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - true
        r2Key:
          type: string
          description: R2 key for the uploaded file
        size:
          type: integer
          description: File size in bytes
        timestamp:
          type: string
          description: ISO 8601 upload timestamp
      required:
        - success
        - r2Key
        - size
        - timestamp
    DeletionRequest:
      type: object
      properties:
        reason:
          type: string
          maxLength: 1000
          description: Reason for account deletion
        confirmEmail:
          type: string
          format: email
          description: Owner email for confirmation
      required:
        - confirmEmail
    DeletionRequestResponse:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - true
        message:
          type: string
          description: Confirmation message
        deletionScheduledFor:
          type: string
          description: ISO 8601 scheduled deletion date
        gracePeriodDays:
          type: integer
          description: Grace period in days
        cancellationDeadline:
          type: string
          description: ISO 8601 deadline for cancellation
      required:
        - success
        - message
        - deletionScheduledFor
        - gracePeriodDays
        - cancellationDeadline
    DeletionCancelResponse:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - true
        message:
          type: string
          description: Confirmation message
      required:
        - success
        - message
    DeletionStatus:
      type: object
      properties:
        isScheduledForDeletion:
          type: boolean
          description: Whether deletion is scheduled
        deletionRequestedAt:
          type: string
          nullable: true
          description: ISO 8601 request timestamp
        deletionScheduledFor:
          type: string
          nullable: true
          description: ISO 8601 scheduled deletion date
        daysRemaining:
          type: number
          nullable: true
          description: Days until permanent deletion
        reason:
          type: string
          nullable: true
          description: Deletion reason
      required:
        - isScheduledForDeletion
        - deletionRequestedAt
        - deletionScheduledFor
        - daysRemaining
        - reason
    MeshRouteProvisionResponse:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - true
        publicUrl:
          type: string
          description: Public URL for the mesh route
        nodeId:
          type: string
          description: Headscale node ID
      required:
        - success
        - publicUrl
        - nodeId
    MeshRouteStatus:
      type: object
      properties:
        status:
          type: string
          enum:
            - disabled
            - pending
            - provisioned
            - failed
          description: Mesh route status
        connectionStatus:
          type: string
          enum:
            - connected
            - disconnected
            - degraded
          description: Live connection status
        publicUrl:
          type: string
          description: Public URL for the mesh route
        meshIp:
          type: string
          description: Mesh IP address
        nodeId:
          type: string
          description: Headscale node ID
        lastCheckAt:
          type: string
          description: ISO 8601 timestamp of last status check
      required:
        - status
    TenantIdPath:
      type: string
      pattern: ^ten_[a-zA-Z0-9]{24}$
      description: Tenant ID in format ten_XXXXXXXXXXXXXXXXXXXX
    DeviceIdPath:
      type: string
      pattern: ^dev_[a-zA-Z0-9]+$
      description: Device ID in format dev_XXX
    AppIdPath:
      type: string
      pattern: ^app_[a-zA-Z0-9]+$
      description: App ID in format app_XXX
    DeploymentIdPath:
      type: string
      pattern: ^dep_[a-zA-Z0-9]+$
      description: Deployment ID in format dep_XXX
    LimitQuery:
      type: integer
      minimum: 1
      maximum: 100
      default: 20
      description: Maximum number of items to return
    OffsetQuery:
      type: integer
      minimum: 0
      default: 0
      description: Number of items to skip
  parameters:
    TenantIdPath:
      schema:
        $ref: "#/components/schemas/TenantIdPath"
      required: true
      description: Tenant ID in format ten_XXXXXXXXXXXXXXXXXXXX
      name: tenantId
      in: path
    DeviceIdPath:
      schema:
        $ref: "#/components/schemas/DeviceIdPath"
      required: true
      description: Device ID in format dev_XXX
      name: deviceId
      in: path
    AppIdPath:
      schema:
        $ref: "#/components/schemas/AppIdPath"
      required: true
      description: App ID in format app_XXX
      name: appId
      in: path
    DeploymentIdPath:
      schema:
        $ref: "#/components/schemas/DeploymentIdPath"
      required: true
      description: Deployment ID in format dep_XXX
      name: deploymentId
      in: path
    LimitQuery:
      schema:
        $ref: "#/components/schemas/LimitQuery"
      required: false
      description: Maximum number of items to return
      name: limit
      in: query
    OffsetQuery:
      schema:
        $ref: "#/components/schemas/OffsetQuery"
      required: false
      description: Number of items to skip
      name: offset
      in: query
paths:
  /health:
    get:
      operationId: getHealth
      tags:
        - Health & Status
      summary: Health check endpoint - returns 200 if service is healthy
      description: Returns overall health status including individual check results for Durable Objects, R2, D1, and other dependencies. Uses 10-second cache.
      security: []
      responses:
        "200":
          description: Service is healthy or degraded
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/FullHealthResponse"
                required:
                  - success
                  - data
        "503":
          description: Service is unhealthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/FullHealthResponse"
                required:
                  - success
                  - data
  /ready:
    get:
      operationId: getReadiness
      tags:
        - Health & Status
      summary: Readiness check endpoint - returns 200 if service is ready
      description: Ultra-fast readiness probe. Only checks critical Durable Object binding. Used by load balancers.
      security: []
      responses:
        "200":
          description: Service is ready
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/ReadinessResponse"
                required:
                  - success
                  - data
        "503":
          description: Service is not ready
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/ReadinessResponse"
                required:
                  - success
                  - data
  /live:
    get:
      operationId: getLiveness
      tags:
        - Health & Status
      summary: Liveness check endpoint - returns 200 if worker is alive
      description: Simple liveness probe. Returns 200 if the Worker isolate is running.
      security: []
      responses:
        "200":
          description: Service is alive
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/LivenessResponse"
                required:
                  - success
                  - data
  /health/deep:
    get:
      operationId: getDeepHealth
      tags:
        - Health & Status
      summary: Deep health check - verifies D1, R2, and billing dependencies
      description: Deep health check that runs fresh checks against all dependencies (D1, R2, Paddle, agent binaries, SLO tracking, circuit breakers). Never cached.
      security: []
      responses:
        "200":
          description: All dependencies healthy or degraded
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/DeepHealthResponse"
                required:
                  - success
                  - data
        "503":
          description: Critical dependencies unhealthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/DeepHealthResponse"
                required:
                  - success
                  - data
  /oidc/{tenantId}/.well-known/openid-configuration:
    get:
      operationId: Oidc.wellKnownOpenidConfigurationByTenantIdGet
      tags:
        - General
      summary: OpenID Connect discovery document
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /oidc/{tenantId}/oauth/jwks:
    get:
      operationId: OidcOauthJwksByTenantIdGet
      tags:
        - General
      summary: JSON Web Key Set for token verification
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /oidc/{tenantId}/oauth/authorize:
    get:
      operationId: OidcOauthAuthorizeByTenantIdGet
      tags:
        - General
      summary: OIDC Authorization Endpoint - initiate authorization code flow with PKCE
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /oidc/{tenantId}/oauth/userinfo:
    get:
      operationId: OidcOauthUserinfoByTenantIdGet
      tags:
        - General
      summary: OIDC UserInfo Endpoint - return claims for authenticated user
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
    post:
      operationId: OidcOauthUserinfoByTenantIdPost
      tags:
        - General
      summary: OIDC UserInfo Endpoint (POST) - return claims for authenticated user
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
    options:
      operationId: OidcOauthUserinfoByTenantIdOptions
      tags:
        - General
      summary: OIDC UserInfo Endpoint - CORS preflight
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /oidc/{tenantId}/oauth/revoke:
    post:
      operationId: OidcOauthRevokeByTenantIdPost
      tags:
        - General
      summary: OAuth 2.0 Token Revocation Endpoint (RFC 7009)
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /oidc/{tenantId}/oauth/end-session:
    get:
      operationId: OidcOauthEndSessionByTenantIdGet
      tags:
        - General
      summary: "OIDC RP-Initiated Logout (end-session) endpoint (Issue #3614)"
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
    post:
      operationId: OidcOauthEndSessionByTenantIdPost
      tags:
        - General
      summary: "OIDC RP-Initiated Logout (end-session) endpoint (Issue #3614)"
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/pairing/initiate:
    post:
      operationId: initiatePairing
      tags:
        - Pairing
      summary: Initiate device pairing - generates pairing code
      description: Start the device pairing flow. The agent calls this with its fingerprint and receives a short pairing code that the user enters in the portal.
      security: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PairingInitiateRequest"
      responses:
        "200":
          description: Pairing code generated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/PairingInitiateResponse"
                required:
                  - success
                  - data
        "400":
          description: Missing or invalid device_fingerprint
        "429":
          description: Rate limit exceeded
  /api/v1/pairing/prepare-activation:
    post:
      operationId: prepareActivation
      tags:
        - Pairing
      summary: Prepare device activation - validates pairing code
      description: Portal calls this after user enters a pairing code. Generates a short-lived activation token for the activate step.
      security: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PrepareActivationRequest"
      responses:
        "200":
          description: Activation token generated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/PrepareActivationResponse"
                required:
                  - success
                  - data
        "400":
          description: Missing pairing_code or tenant_id, or code not pending
        "404":
          description: Invalid pairing code
  /api/v1/pairing/activate:
    post:
      operationId: activatePairing
      tags:
        - Pairing
      summary: Activate device - completes pairing flow
      description: Complete the pairing flow. Creates the device, enrolls it in TenantState, and returns the shared secret and infrastructure secrets.
      security: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PairingActivateRequest"
      responses:
        "200":
          description: Device activated and enrolled
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/PairingActivateResponse"
                required:
                  - success
                  - data
        "400":
          description: Missing required fields or invalid email
        "429":
          description: Rate limit exceeded
  /api/v1/pairing/auto-activate:
    post:
      operationId: autoActivatePairing
      tags:
        - Pairing
      summary: Auto-activate device pairing via enrollment token (install script flow)
      description: Automated pairing activation for the install script flow. Validates the enrollment token, generates an activation token, and completes the pairing in one call.
      security: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AutoActivateRequest"
      responses:
        "200":
          description: Device auto-activated and enrolled
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/PairingActivateResponse"
                required:
                  - success
                  - data
        "404":
          description: Invalid or expired enrollment token or pairing code
        "429":
          description: Rate limit exceeded
  /api/v1/pairing/status/{fingerprint}:
    get:
      operationId: getPairingStatus
      tags:
        - Pairing
      summary: Get pairing status by device fingerprint
      description: Check pairing status for a device by its fingerprint. The agent polls this to know when the user has completed activation.
      security: []
      parameters:
        - schema:
            type: string
          required: true
          name: fingerprint
          in: path
      responses:
        "200":
          description: Pairing status
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/PairingStatusResponse"
                required:
                  - success
                  - data
        "400":
          description: Fingerprint required
        "429":
          description: Rate limit exceeded
  /api/v1/enrollment/{token}/progress:
    get:
      operationId: getEnrollmentProgress
      tags:
        - Pairing
      summary: Get enrollment progress by token
      description: Retrieve enrollment progress for a device by its enrollment token. Used by the portal to show real-time install progress.
      security: []
      parameters:
        - schema:
            type: string
          required: true
          name: token
          in: path
      responses:
        "200":
          description: Enrollment progress
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/EnrollmentProgress"
                required:
                  - success
                  - data
        "404":
          description: Enrollment not found
        "429":
          description: Rate limit exceeded
    patch:
      operationId: updateEnrollmentProgress
      tags:
        - Pairing
      summary: Update enrollment progress from install script (unauthenticated, token-scoped)
      description: Update enrollment progress from the install script. Unauthenticated but scoped to a specific enrollment token.
      security: []
      parameters:
        - schema:
            type: string
          required: true
          name: token
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EnrollmentProgressUpdateRequest"
      responses:
        "200":
          description: Progress updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "404":
          description: Enrollment not found
  /downloads/install/{token}:
    get:
      operationId: DownloadsInstallByTokenGet
      tags:
        - General
      summary: Serve templated install script with enrollment token (public, rate-limited)
      parameters:
        - schema:
            type: string
          required: true
          name: token
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /downloads/{filename}:
    get:
      operationId: DownloadsByFilenameGet
      tags:
        - General
      summary: Download agent binary or metadata (public, rate-limited)
      parameters:
        - schema:
            type: string
          required: true
          name: filename
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /internal/secret-hashes:
    get:
      operationId: InternalSecretHashesGet
      tags:
        - Internal
      summary: Returns SHA-256 hashes of deployed secrets for verification (requires PROMETHEUS_BEARER_TOKEN)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/users/by-email/{email}:
    get:
      operationId: apiV1InternalUsersByEmailByEmailGet
      tags:
        - Internal
      summary: Look up user by email address (requires x-ub-internal-api-key). Returns tenantId, tier, and name.
      parameters:
        - schema:
            type: string
          required: true
          name: email
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/terraform/lock:
    post:
      operationId: apiV1TerraformLockPost
      tags:
        - General
      summary: Acquire Terraform state lock
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/terraform/lock/{lockId}:
    delete:
      operationId: apiV1TerraformLockByLockIdDelete
      tags:
        - General
      summary: Release Terraform state lock
      parameters:
        - schema:
            type: string
          required: true
          name: lockId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
    get:
      operationId: apiV1TerraformLockByLockIdGet
      tags:
        - General
      summary: Get Terraform state lock status
      parameters:
        - schema:
            type: string
          required: true
          name: lockId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/terraform/lock/{lockId}/renew:
    post:
      operationId: apiV1TerraformLockRenewByLockIdPost
      tags:
        - General
      summary: Renew Terraform state lock TTL
      parameters:
        - schema:
            type: string
          required: true
          name: lockId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/terraform/lock/{lockId}/audit:
    get:
      operationId: apiV1TerraformLockAuditByLockIdGet
      tags:
        - General
      summary: Get Terraform state lock audit log
      parameters:
        - schema:
            type: string
          required: true
          name: lockId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /metrics:
    get:
      operationId: MetricsGet
      tags:
        - General
      summary: Prometheus metrics endpoint (requires PROMETHEUS_BEARER_TOKEN in production)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /internal/metrics:
    get:
      operationId: InternalMetricsGet
      tags:
        - Internal
      summary: Internal metrics endpoint (requires PROMETHEUS_BEARER_TOKEN in production)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /internal/metrics/latency:
    get:
      operationId: InternalMetricsLatencyGet
      tags:
        - Internal
      summary: Latency profiler overview with global p50/p95/p99 (requires auth in production)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /internal/metrics/latency/endpoints:
    get:
      operationId: InternalMetricsLatencyEndpointsGet
      tags:
        - Internal
      summary: Per-endpoint latency breakdown with histograms (requires auth in production)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /internal/metrics/latency/prometheus:
    get:
      operationId: InternalMetricsLatencyPrometheusGet
      tags:
        - Internal
      summary: Prometheus-compatible latency histogram metrics (requires auth in production)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /internal/metrics/queries:
    get:
      operationId: InternalMetricsQueriesGet
      tags:
        - Internal
      summary: Query performance monitor with N+1 detection (requires auth in production)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /internal/metrics/queries/prometheus:
    get:
      operationId: InternalMetricsQueriesPrometheusGet
      tags:
        - Internal
      summary: Prometheus-compatible query metrics (requires auth in production)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/metrics/rum:
    post:
      operationId: apiLegacyMetricsRumPost
      tags:
        - General
      summary: Ingest RUM data from portal clients
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/tenants:
    get:
      operationId: apiV1AdminTenantsGet
      tags:
        - Tenants
      summary: List all tenants (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/vault/health:
    get:
      operationId: apiV1AdminVaultHealthGet
      tags:
        - Health & Status
      summary: Check Vault health status (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/vault/audit/summary:
    get:
      operationId: apiV1AdminVaultAuditSummaryGet
      tags:
        - Admin
      summary: Get Vault audit summary (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/metrics/system:
    get:
      operationId: apiV1AdminMetricsSystemGet
      tags:
        - Admin
      summary: Get system-wide metrics (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/tenants/summary:
    get:
      operationId: apiV1AdminTenantsSummaryGet
      tags:
        - Tenants
      summary: Get tenants summary (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/devices/summary:
    get:
      operationId: apiV1AdminDevicesSummaryGet
      tags:
        - Devices
      summary: Get devices summary (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/deployments/summary:
    get:
      operationId: apiV1AdminDeploymentsSummaryGet
      tags:
        - Deployments
      summary: Get deployments summary (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/metrics/requests:
    get:
      operationId: apiV1AdminMetricsRequestsGet
      tags:
        - Admin
      summary: Get time-series requests metrics (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/metrics/errors:
    get:
      operationId: apiV1AdminMetricsErrorsGet
      tags:
        - Admin
      summary: Get time-series errors metrics (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/metrics/resources:
    get:
      operationId: apiV1AdminMetricsResourcesGet
      tags:
        - Admin
      summary: Get time-series resources metrics (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/security-health:
    get:
      operationId: apiV1AdminSecurityHealthGet
      tags:
        - Security Alerts
      summary: Get security system health metrics (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/bootstrap-tokens/reset:
    post:
      operationId: apiV1AdminBootstrapTokensResetPost
      tags:
        - Admin
      summary: Reset bootstrap token store (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/tenants/{tenantId}:
    delete:
      operationId: apiV1AdminTenantsByTenantIdDelete
      tags:
        - Tenants
      summary: Delete a tenant (admin only)
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/observability/tenant-costs:
    get:
      operationId: apiV1AdminObservabilityTenantCostsGet
      tags:
        - Admin
      summary: Get per-tenant resource consumption (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/observability/storage-trends:
    get:
      operationId: apiV1AdminObservabilityStorageTrendsGet
      tags:
        - Admin
      summary: Get per-tenant storage trending data (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/oidc/signing-keys/rotate:
    post:
      operationId: apiV1OidcSigningKeysRotatePost
      tags:
        - General
      summary: Rotate OIDC signing keys for a tenant (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/canaries/status:
    get:
      operationId: apiV1AdminCanariesStatusGet
      tags:
        - Admin
      summary: Get canary token system status (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/canaries/plant:
    post:
      operationId: apiV1AdminCanariesPlantPost
      tags:
        - Admin
      summary: Manually plant canary tokens (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/canaries/rotate:
    post:
      operationId: apiV1AdminCanariesRotatePost
      tags:
        - Admin
      summary: Manually rotate all canary tokens (admin only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/admin/costs/summary:
    get:
      operationId: getCostSummary
      tags:
        - Cost
      summary: Platform-wide cost summary
      description: Get platform-wide cost summary with D1 and R2 breakdowns. Admin endpoint.
      responses:
        "200":
          description: Cost summary
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/CostSummary"
                required:
                  - success
                  - data
  /api/admin/costs/tenant/{tenantId}:
    get:
      operationId: getTenantCosts
      tags:
        - Cost
      summary: Tenant-specific cost breakdown
      description: Get cost breakdown for a specific tenant. Admin endpoint.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Tenant cost breakdown
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/TenantCost"
                required:
                  - success
                  - data
        "400":
          description: Tenant ID required
  /api/admin/costs/forecast:
    get:
      operationId: getCostForecast
      tags:
        - Cost
      summary: Cost forecasting
      description: Get cost forecasting based on usage trends. Admin endpoint.
      responses:
        "200":
          description: Cost forecast
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/CostForecast"
                required:
                  - success
                  - data
  /api/admin/costs/d1/expensive-queries:
    get:
      operationId: getExpensiveQueries
      tags:
        - Cost
      summary: Top expensive D1 queries
      description: Get top expensive D1 queries by cost and duration. Admin endpoint.
      responses:
        "200":
          description: Expensive queries
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/ExpensiveQuery"
                required:
                  - success
                  - data
  /api/admin/costs/r2/lifecycle-status:
    get:
      operationId: getLifecycleStatus
      tags:
        - Cost
      summary: R2 lifecycle compliance status
      description: Get R2 lifecycle policy compliance status. Admin endpoint.
      responses:
        "200":
          description: Lifecycle status
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/LifecycleStatus"
                required:
                  - success
                  - data
  /api/admin/costs/r2/enforce-lifecycle:
    post:
      operationId: enforceLifecycle
      tags:
        - Cost
      summary: Trigger lifecycle enforcement
      description: Manually trigger R2 lifecycle policy enforcement. Admin endpoint.
      responses:
        "200":
          description: Enforcement result
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
  /api/v1/cost/status:
    get:
      operationId: getCostStatus
      tags:
        - Cost
      summary: Get cost status for authenticated tenant with budget information
      description: Get cost status with budget information for the authenticated tenant.
      responses:
        "200":
          description: Cost status with budget
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/CostStatus"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/admin/dlq/stats:
    get:
      operationId: apiLegacyAdminDlqStatsGet
      tags:
        - Admin
      summary: Get DLQ retry queue statistics
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/admin/dlq/entries:
    get:
      operationId: apiLegacyAdminDlqEntriesGet
      tags:
        - Admin
      summary: Get DLQ entries ready for retry
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/admin/dlq/replay:
    post:
      operationId: apiLegacyAdminDlqReplayPost
      tags:
        - Admin
      summary: Manually trigger smart DLQ replay
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/feature-flags:
    get:
      operationId: apiV1AdminFeatureFlagsGet
      tags:
        - Admin
      summary: List all feature flags
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
    post:
      operationId: apiV1AdminFeatureFlagsPost
      tags:
        - Admin
      summary: Create a new feature flag
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/feature-flags/evaluate:
    post:
      operationId: apiV1AdminFeatureFlagsEvaluatePost
      tags:
        - Admin
      summary: Evaluate flags with context
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/feature-flags/overrides:
    post:
      operationId: apiV1AdminFeatureFlagsOverridesPost
      tags:
        - Admin
      summary: Set a tenant override
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/feature-flags/rules:
    post:
      operationId: apiV1AdminFeatureFlagsRulesPost
      tags:
        - Admin
      summary: Create a targeting rule
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/feature-flags/rules/{ruleId}:
    delete:
      operationId: apiV1AdminFeatureFlagsRulesByRuleIdDelete
      tags:
        - Admin
      summary: Delete a targeting rule
      parameters:
        - schema:
            type: string
          required: true
          name: ruleId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/feature-flags/{flagId}:
    get:
      operationId: apiV1AdminFeatureFlagsByFlagIdGet
      tags:
        - Admin
      summary: Get feature flag details
      parameters:
        - schema:
            type: string
          required: true
          name: flagId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
    patch:
      operationId: apiV1AdminFeatureFlagsByFlagIdPatch
      tags:
        - Admin
      summary: Update a feature flag
      parameters:
        - schema:
            type: string
          required: true
          name: flagId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
    delete:
      operationId: apiV1AdminFeatureFlagsByFlagIdDelete
      tags:
        - Admin
      summary: Delete a feature flag
      parameters:
        - schema:
            type: string
          required: true
          name: flagId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/feature-flags/{flagId}/toggle:
    post:
      operationId: apiV1AdminFeatureFlagsToggleByFlagIdPost
      tags:
        - Admin
      summary: Toggle feature flag enabled state
      parameters:
        - schema:
            type: string
          required: true
          name: flagId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/feature-flags/{flagId}/overrides:
    get:
      operationId: apiV1AdminFeatureFlagsOverridesByFlagIdGet
      tags:
        - Admin
      summary: Get overrides for a flag
      parameters:
        - schema:
            type: string
          required: true
          name: flagId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/feature-flags/{flagId}/overrides/{tenantId}:
    delete:
      operationId: apiV1AdminFeatureFlagsOverridesByFlagIdByTenantIdDelete
      tags:
        - Admin
      summary: Remove a tenant override
      parameters:
        - schema:
            type: string
          required: true
          name: flagId
          in: path
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/feature-flags/{flagId}/rules:
    get:
      operationId: apiV1AdminFeatureFlagsRulesByFlagIdGet
      tags:
        - Admin
      summary: Get rules for a flag
      parameters:
        - schema:
            type: string
          required: true
          name: flagId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/admin/feature-flags/{flagId}/audit:
    get:
      operationId: apiV1AdminFeatureFlagsAuditByFlagIdGet
      tags:
        - Admin
      summary: Get audit log for a flag
      parameters:
        - schema:
            type: string
          required: true
          name: flagId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/traces:
    get:
      operationId: apiV1TracesGet
      tags:
        - Traces
      summary: Query traces with filters
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/traces/summary:
    get:
      operationId: apiV1TracesSummaryGet
      tags:
        - Traces
      summary: Get trace summary statistics
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/traces/slow:
    get:
      operationId: apiV1TracesSlowGet
      tags:
        - Traces
      summary: Get slow traces above threshold
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/traces/errors:
    get:
      operationId: apiV1TracesErrorsGet
      tags:
        - Traces
      summary: Get recent error traces
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/traces/metrics:
    get:
      operationId: apiV1TracesMetricsGet
      tags:
        - Traces
      summary: Get service metrics from traces
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/traces/cleanup:
    delete:
      operationId: apiV1TracesCleanupDelete
      tags:
        - Traces
      summary: Clean up old traces
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/traces/{traceId}:
    get:
      operationId: apiV1TracesByTraceIdGet
      tags:
        - Traces
      summary: Get trace with all spans
      parameters:
        - schema:
            type: string
          required: true
          name: traceId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/observability/logs:
    post:
      operationId: apiV1ObservabilityLogsPost
      tags:
        - General
      summary: Query logs for tenant - requires portal auth
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/observability/logs/aggregation:
    post:
      operationId: apiV1ObservabilityLogsAggregationPost
      tags:
        - General
      summary: Get aggregated log statistics - requires portal auth
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/observability/metrics:
    get:
      operationId: apiV1ObservabilityMetricsGet
      tags:
        - General
      summary: Get observability metrics for tenant - requires portal auth
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/sessions:
    post:
      operationId: apiV1InternalSessionsPost
      tags:
        - Internal
      summary: Create a new session (internal only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/sessions/{tenantId}/{sessionId}:
    get:
      operationId: apiV1InternalSessionsByTenantIdBySessionIdGet
      tags:
        - Internal
      summary: Get a session by ID (internal only)
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: sessionId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
    delete:
      operationId: apiV1InternalSessionsByTenantIdBySessionIdDelete
      tags:
        - Internal
      summary: Delete a session (internal only)
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: sessionId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/sessions/{tenantId}/{sessionId}/refresh:
    post:
      operationId: apiV1InternalSessionsRefreshByTenantIdBySessionIdPost
      tags:
        - Internal
      summary: Refresh session expiration (internal only)
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: sessionId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/sessions/{tenantId}/stats:
    get:
      operationId: apiV1InternalSessionsStatsByTenantIdGet
      tags:
        - Internal
      summary: Get session statistics (internal only)
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/sessions/{tenantId}/cleanup:
    post:
      operationId: apiV1InternalSessionsCleanupByTenantIdPost
      tags:
        - Internal
      summary: Force session cleanup (internal only)
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/users/lookup:
    post:
      operationId: apiV1InternalUsersLookupPost
      tags:
        - Internal
      summary: Look up user by email (internal only)
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/users/{userId}/mfa/setup:
    post:
      operationId: apiV1InternalUsersMfaSetupByUserIdPost
      tags:
        - Internal
      summary: Generate TOTP secret for MFA setup (internal only)
      parameters:
        - schema:
            type: string
          required: true
          name: userId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/users/{userId}/mfa/verify:
    post:
      operationId: apiV1InternalUsersMfaVerifyByUserIdPost
      tags:
        - Internal
      summary: Verify TOTP code (internal only)
      parameters:
        - schema:
            type: string
          required: true
          name: userId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/users/{userId}/mfa/disable:
    post:
      operationId: apiV1InternalUsersMfaDisableByUserIdPost
      tags:
        - Internal
      summary: Disable MFA and delete TOTP secret (requires TOTP re-auth, internal only)
      parameters:
        - schema:
            type: string
          required: true
          name: userId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/users/{userId}/mfa/recovery:
    post:
      operationId: apiV1InternalUsersMfaRecoveryByUserIdPost
      tags:
        - Internal
      summary: Verify MFA recovery code (internal only)
      parameters:
        - schema:
            type: string
          required: true
          name: userId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/users/{userId}/mfa/status:
    get:
      operationId: apiV1InternalUsersMfaStatusByUserIdGet
      tags:
        - Internal
      summary: Get MFA status and recovery codes remaining (internal only)
      parameters:
        - schema:
            type: string
          required: true
          name: userId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/users/{userId}/mfa/reenroll:
    post:
      operationId: apiV1InternalUsersMfaReenrollByUserIdPost
      tags:
        - Internal
      summary: Re-enroll MFA - disables current, redirects to enrollment (internal only)
      parameters:
        - schema:
            type: string
          required: true
          name: userId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/users/{userId}/mfa/recovery-codes/regenerate:
    post:
      operationId: apiV1InternalUsersMfaRecoveryCodesRegenerateByUserIdPost
      tags:
        - Internal
      summary: Regenerate recovery codes (requires TOTP confirmation, internal only)
      parameters:
        - schema:
            type: string
          required: true
          name: userId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/internal/users/{userId}/mfa/enroll/complete:
    post:
      operationId: apiV1InternalUsersMfaEnrollCompleteByUserIdPost
      tags:
        - Internal
      summary: Mark MFA enrollment as complete (internal only)
      parameters:
        - schema:
            type: string
          required: true
          name: userId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/hosts/{deviceId}/update-policy:
    patch:
      operationId: setUpdatePolicy
      tags:
        - Fleet Management
      summary: Set device update policy (auto/pinned/rollback) - requires portal auth
      description: Set the update policy for a device. Supports auto, pinned (to a specific version), or rollback mode.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SetUpdatePolicyRequest"
      responses:
        "200":
          description: Policy updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/UpdatePolicy"
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "404":
          description: Device not found
    get:
      operationId: getUpdatePolicy
      tags:
        - Fleet Management
      summary: Get device update policy - requires portal auth
      description: Get the current update policy for a device. Returns default 'auto' policy if none is explicitly set.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      responses:
        "200":
          description: Current update policy
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/UpdatePolicy"
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/v1/deployments/{deploymentId}/rollback-agents:
    post:
      operationId: rollbackDeploymentAgents
      tags:
        - Fleet Management
      summary: Trigger rollback for all agents in a deployment - requires portal auth
      description: Set rollback policy for all devices in a deployment, triggering them to roll back to the specified version.
      parameters:
        - schema:
            type: string
          required: true
          name: deploymentId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RollbackAgentsRequest"
      responses:
        "200":
          description: Rollback triggered
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/RollbackAgentsResponse"
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "404":
          description: No devices found for deployment
  /api/v1/hosts/{deviceId}/update-check:
    get:
      operationId: agentUpdateCheck
      tags:
        - Fleet Management
      summary: Agent checks update policy - requires HMAC auth
      description: Agent-facing endpoint to check the current update policy and version info. Authenticated via HMAC headers.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      responses:
        "200":
          description: Update policy for agent
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/UpdateCheckResponse"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/hosts/{deviceId}/rollback-ack:
    post:
      operationId: agentRollbackAck
      tags:
        - Fleet Management
      summary: Agent acknowledges completed rollback, clearing rollback policy - requires HMAC auth
      description: Agent acknowledges successful rollback completion. Clears the rollback policy back to 'auto' to prevent infinite rollback loops.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      responses:
        "204":
          description: Rollback acknowledged
        "401":
          description: Unauthorized
  /api/v1/roles:
    get:
      operationId: listRoles
      tags:
        - Roles
      summary: List users and their roles for the calling tenant
      description: List all users and their role assignments for the calling tenant. Limited to 500 results.
      responses:
        "200":
          description: Role list
          content:
            application/json:
              schema:
                type: object
                properties:
                  roles:
                    type: array
                    items:
                      $ref: "#/components/schemas/RoleAssignment"
                required:
                  - roles
        "401":
          description: Unauthorized
        "403":
          description: Forbidden — requires owner or admin role
  /api/v1/roles/me:
    get:
      operationId: getMyRole
      tags:
        - Roles
      summary: Get the calling user's role
      description: Get the calling user's role within the tenant. Returns 'owner' for API key auth.
      responses:
        "200":
          description: User role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MyRoleResponse"
        "401":
          description: Unauthorized
  /api/v1/roles/{userId}:
    put:
      operationId: setUserRole
      tags:
        - Roles
      summary: Set a user's role (owner only)
      description: Set or update a user's role within the tenant. Only owners can assign roles. Multiple owners are allowed.
      parameters:
        - schema:
            type: string
          required: true
          name: userId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SetRoleRequest"
      responses:
        "200":
          description: Role updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SetRoleResponse"
        "400":
          description: Invalid request or self-demotion attempt
        "401":
          description: Unauthorized
        "403":
          description: Forbidden — requires owner role
    delete:
      operationId: removeUserRole
      tags:
        - Roles
      summary: Remove a user's role assignment (owner only)
      description: Remove a user's role assignment from the tenant. Cannot remove self or other owners.
      parameters:
        - schema:
            type: string
          required: true
          name: userId
          in: path
      responses:
        "204":
          description: Role removed
        "400":
          description: Cannot remove self or owner
        "401":
          description: Unauthorized
        "403":
          description: Forbidden — requires owner role
        "404":
          description: User role not found
  /api/v1/hosts/{deviceId}/commands:
    get:
      operationId: getDeviceCommands
      tags:
        - Agent
      summary: Agent polls for pending commands - requires HMAC auth
      description: Agent polls for pending commands queued for the device. Requires HMAC authentication via x-ub-* headers.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      responses:
        "200":
          description: Pending commands
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/CommandList"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized - missing or invalid HMAC
        "429":
          description: Rate limit exceeded
    post:
      operationId: queueDeviceCommand
      tags:
        - Agent
      summary: Portal queues command for agent - requires portal auth
      description: Queue a command for a device agent to execute. Requires portal auth.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PostCommandRequest"
      responses:
        "201":
          description: Command queued
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/Command"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
        "429":
          description: Rate limit exceeded
  /api/v1/hosts/{deviceId}/commands/{commandId}/ack:
    post:
      operationId: acknowledgeCommand
      tags:
        - Agent
      summary: Agent acknowledges command execution - requires HMAC auth
      description: Agent acknowledges command execution with status and output. Requires HMAC auth.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
        - schema:
            type: string
          required: true
          name: commandId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CommandAckRequest"
      responses:
        "200":
          description: Acknowledgement received
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "401":
          description: Unauthorized - HMAC verification failed
        "404":
          description: Command not found
  /api/v1/hosts/{deviceId}/heartbeat:
    post:
      operationId: sendHeartbeat
      tags:
        - Agent
      summary: Agent heartbeat with status - requires HMAC auth
      description: Agent sends heartbeat with status, metrics, and app states. Requires HMAC auth.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HeartbeatRequest"
      responses:
        "200":
          description: Heartbeat acknowledged
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/HeartbeatResponse"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized - HMAC verification failed
        "429":
          description: Rate limit exceeded
  /api/v1/hosts/{deviceId}/public-key:
    get:
      operationId: getDevicePublicKey
      tags:
        - Agent
      summary: "Get agent E2EE public key for ECDH key exchange - Issue #1151"
      description: Get the agent's E2EE public key (JWK format) for ECDH key exchange. Used by Portal to encrypt commands.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      responses:
        "200":
          description: Agent public key
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/PublicKey"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/logs:
    post:
      operationId: forwardLogs
      tags:
        - Agent
      summary: Agent log forwarding endpoint - forwards logs to dual-destination pipeline (R2 + Log Explorer)
      description: Forward batched log entries from agent to dual-destination pipeline (R2 + Log Explorer). Requires HMAC auth.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LogForwardRequest"
      responses:
        "200":
          description: Logs accepted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/LogForwardResponse"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
        "429":
          description: Rate limit exceeded
  /api/v1/hosts/{deviceId}/logs:
    post:
      operationId: forwardDeviceLogs
      tags:
        - Agent
      summary: Agent log forwarding endpoint with deviceId in path - forwards logs to dual-destination pipeline
      description: Forward batched log entries from a specific device agent. Requires HMAC auth.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LogForwardRequest"
      responses:
        "200":
          description: Logs accepted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/LogForwardResponse"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
        "429":
          description: Rate limit exceeded
  /api/v1/backups:
    get:
      operationId: listBackups
      tags:
        - Backups
      summary: List all backups for tenant - requires portal auth
      description: List all backups for the authenticated tenant.
      responses:
        "200":
          description: Backup list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Backup"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/hosts/{deviceId}/backups:
    get:
      operationId: listDeviceBackups
      tags:
        - Backups
      summary: List backups for specific device - requires portal auth
      description: List backups for a specific device.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      responses:
        "200":
          description: Device backup list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Backup"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    post:
      operationId: notifyBackupCreated
      tags:
        - Backups
      summary: Notify backup creation - requires agent HMAC auth
      description: Agent notifies orchestrator of a completed backup. Requires HMAC auth.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      responses:
        "201":
          description: Backup recorded
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/Backup"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/hosts/{deviceId}/backups/{backupId}/upload:
    post:
      operationId: uploadBackup
      tags:
        - Backups
      summary: Upload backup data - requires agent HMAC auth
      description: Upload backup data to R2 storage. Requires agent HMAC auth.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
        - schema:
            type: string
          required: true
          name: backupId
          in: path
      responses:
        "200":
          description: Upload accepted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "401":
          description: Unauthorized - HMAC verification failed
        "413":
          description: Payload too large
  /api/v1/backups/{backupId}/download:
    get:
      operationId: downloadBackup
      tags:
        - Backups
      summary: Download backup - agent (with deviceId) or portal auth
      description: Download a backup. Portal users get direct download; agents specify ?deviceId for HMAC-authenticated access.
      parameters:
        - schema:
            type: string
          required: true
          name: backupId
          in: path
      responses:
        "200":
          description: Backup data stream
        "401":
          description: Unauthorized
        "404":
          description: Backup not found
  /api/v1/hosts/{deviceId}/backups/{backupId}/verify:
    post:
      operationId: verifyBackup
      tags:
        - Backups
      summary: Verify backup integrity - requires agent HMAC auth
      description: Verify the integrity of a backup. Requires agent HMAC auth.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
        - schema:
            type: string
          required: true
          name: backupId
          in: path
      responses:
        "200":
          description: Verification result
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      verified:
                        type: boolean
                      details:
                        type: object
                        additionalProperties: {}
                    required:
                      - verified
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Backup not found
  /api/v1/hosts/{deviceId}/backups/{backupId}/restore:
    post:
      operationId: restoreBackup
      tags:
        - Backups
      summary: Restore backup to device - requires portal auth
      description: Initiate a backup restore to a device. Requires portal auth.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
        - schema:
            type: string
          required: true
          name: backupId
          in: path
      responses:
        "200":
          description: Restore initiated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/RestoreJob"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Backup not found
  /api/v1/backup-drills:
    get:
      operationId: listBackupDrills
      tags:
        - Backups
      summary: List backup drills for tenant - requires portal auth
      description: List backup drill runs for the tenant.
      responses:
        "200":
          description: Backup drills
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/BackupDrill"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/backup-drills/{drillId}:
    get:
      operationId: getBackupDrill
      tags:
        - Backups
      summary: Get backup drill details - requires portal auth
      description: Get details of a specific backup drill.
      parameters:
        - schema:
            type: string
          required: true
          name: drillId
          in: path
      responses:
        "200":
          description: Backup drill details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BackupDrill"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Drill not found
  /api/v1/backup-drills/trigger:
    post:
      operationId: triggerBackupDrill
      tags:
        - Backups
      summary: Trigger manual backup drill - requires portal auth
      description: Manually trigger a backup drill for the tenant.
      responses:
        "201":
          description: Drill triggered
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BackupDrill"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/backup-drill-config:
    get:
      operationId: getBackupDrillConfig
      tags:
        - Backups
      summary: Get backup drill configuration - requires portal auth
      description: Get the backup drill configuration for the tenant.
      responses:
        "200":
          description: Drill configuration
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BackupDrillConfig"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    put:
      operationId: updateBackupDrillConfig
      tags:
        - Backups
      summary: Update backup drill configuration - requires portal auth
      description: Update the backup drill configuration (schedule, retention, etc.).
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BackupDrillConfig"
      responses:
        "200":
          description: Configuration updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BackupDrillConfig"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
  /api/v1/backup-verification-summary:
    get:
      operationId: getBackupVerificationSummary
      tags:
        - Backups
      summary: Get backup verification summary from BackupManager - requires portal auth
      description: Get a summary of backup verification status across all backups.
      responses:
        "200":
          description: Verification summary
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BackupVerificationSummary"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/backup-policies:
    put:
      operationId: updateBackupPolicies
      tags:
        - Backups
      summary: Update backup policies for tenant - requires portal auth
      description: Bulk update backup policies for the tenant.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                policies:
                  type: array
                  items:
                    $ref: "#/components/schemas/BackupPolicy"
                  description: Updated policies
              required:
                - policies
      responses:
        "200":
          description: Policies updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/BackupPolicy"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
    get:
      operationId: listBackupPolicies
      tags:
        - Backups
      summary: List backup policies
      description: List all backup policies for the tenant.
      responses:
        "200":
          description: Backup policies
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/BackupPolicy"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    post:
      operationId: createBackupPolicy
      tags:
        - Backups
      summary: Create backup policy
      description: Create a new backup policy with schedule and retention.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateBackupPolicyRequest"
      responses:
        "201":
          description: Backup policy created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BackupPolicy"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
  /api/v1/backup-alerts:
    get:
      operationId: getBackupAlerts
      tags:
        - Backups
      summary: Get backup alerts from HealthManager - requires portal auth
      description: Get backup-related alerts from the HealthManager.
      responses:
        "200":
          description: Backup alerts
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/HealthAlert"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/backup-metrics:
    get:
      operationId: getBackupMetrics
      tags:
        - Backups
      summary: Get backup metrics from BackupManager - requires portal auth
      description: Get backup metrics (size, count, success rate) from BackupManager.
      responses:
        "200":
          description: Backup metrics
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BackupMetrics"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/backup-monitoring:
    get:
      operationId: getBackupMonitoring
      tags:
        - Backups
      summary: Get combined backup monitoring (fan-out to HealthManager + BackupManager) - requires portal auth
      description: Get combined backup monitoring data from HealthManager and BackupManager.
      responses:
        "200":
          description: Backup monitoring data
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BackupMonitoring"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/restore-jobs:
    get:
      operationId: listRestoreJobs
      tags:
        - Backups
      summary: List restore jobs from BackupManager - requires portal auth
      description: List all restore jobs for the tenant.
      responses:
        "200":
          description: Restore jobs
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/RestoreJob"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/invites:
    get:
      operationId: listInvites
      tags:
        - Invites
      summary: List all invites for a tenant (admin)
      description: List all invites for the tenant, ordered by creation date descending.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Invite list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Invite"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "403":
          description: Forbidden — requires owner or admin role
    post:
      operationId: createInvite
      tags:
        - Invites
      summary: Create and send a tenant invite (admin)
      description: Create a new invite for the tenant. Only owners can create owner-role invites. Returns the invite code for sharing.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateInviteRequest"
      responses:
        "201":
          description: Invite created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/CreateInviteResponse"
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden — cannot escalate to owner role
        "409":
          description: Pending invite exists or user already a member
  /api/v1/tenants/{tenantId}/invites/{inviteId}:
    delete:
      operationId: revokeInvite
      tags:
        - Invites
      summary: Revoke a pending invite (admin)
      description: Revoke a pending invite. Only pending invites can be revoked.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: inviteId
          in: path
      responses:
        "200":
          description: Invite revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                required:
                  - success
        "400":
          description: Cannot revoke non-pending invite
        "401":
          description: Unauthorized
        "404":
          description: Invite not found
  /api/v1/invites/{token}/validate:
    get:
      operationId: validateInvite
      tags:
        - Invites
      summary: Validate an invite token (public)
      description: Validate an invite token and return invite details. Public endpoint — no authentication required.
      parameters:
        - schema:
            type: string
          required: true
          name: token
          in: path
      responses:
        "200":
          description: Valid invite
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/ValidateInviteResponse"
                required:
                  - success
                  - data
        "400":
          description: Invalid invite link
        "404":
          description: Invite not found
        "410":
          description: Invite expired, revoked, or already accepted
  /api/v1/invites/{token}/accept:
    post:
      operationId: acceptInvite
      tags:
        - Invites
      summary: Accept an invite and create account (public)
      description: Accept an invite token and create an account or add tenant membership. Public endpoint — no authentication required.
      parameters:
        - schema:
            type: string
          required: true
          name: token
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AcceptInviteRequest"
      responses:
        "200":
          description: Invite accepted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AcceptInviteResponse"
        "400":
          description: Invalid invite link
        "404":
          description: Invite not found
        "410":
          description: Invite expired, revoked, or already accepted
  /api/v1/check-slug:
    get:
      operationId: checkSlugAvailability
      tags:
        - Tenants
      summary: Check if a tenant slug is available
      description: Check if a tenant slug is available for registration. Rate-limited by IP.
      parameters:
        - name: slug
          in: query
          required: true
          schema:
            type: string
            pattern: "^[a-z0-9-]+$"
            maxLength: 63
      responses:
        "200":
          description: Slug availability result
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      available:
                        type: boolean
                    required:
                      - available
                required:
                  - success
                  - data
        "400":
          description: Invalid slug format
  /api/v1/tenants:
    post:
      operationId: createTenant
      tags:
        - Tenants
      summary: Create new tenant (bootstrap auth)
      description: Create a new tenant. Supports bootstrap token auth for first-time setup. Tenant ID is generated idempotently from user ID hash.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateTenantRequest"
      responses:
        "201":
          description: Tenant created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/CreateTenantResponse"
                required:
                  - success
                  - data
        "400":
          description: Invalid request body
  /api/v1/tenants/{tenantId}:
    get:
      operationId: getTenant
      tags:
        - Tenants
      summary: Get tenant details
      description: Get tenant details including deployment, app, and tunnel counts. Allows unauthenticated access (tenant ID is derived from OIDC user ID).
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Tenant details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/Tenant"
                required:
                  - success
                  - data
        "401":
          description: Tenant not found (returns 401 to prevent ID enumeration)
    put:
      operationId: updateTenant
      tags:
        - Tenants
      summary: Update tenant
      description: Update tenant configuration (name, slug, plan). Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateTenantRequest"
      responses:
        "200":
          description: Tenant updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "400":
          description: Invalid request body
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found
    delete:
      operationId: deleteTenant
      tags:
        - Tenants
      summary: Delete tenant
      description: Delete a tenant from the registry, clear its verification cache, and reset its TenantState. Bootstrap-authed, mirroring tenant creation. Idempotent — a tenant already absent from the registry returns success.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Tenant deleted (or already absent)
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      message:
                        type: string
                      tenantId:
                        type: string
                    required:
                      - message
                      - tenantId
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "500":
          description: Deletion failed
  /api/v1/tenants/{tenantId}/apps:
    get:
      operationId: listTenantApps
      tags:
        - Tenants
      summary: List apps for tenant
      description: List all apps deployed within the tenant. Forwarded to TenantState Durable Object.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: App list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/AppSummary"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/apps/{appId}/secrets:
    get:
      operationId: getTenantAppSecrets
      tags:
        - Tenants
      summary: Get app secrets for tenant
      description: Get secrets for a specific app within the tenant. Forwarded to TenantState Durable Object.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: appId
          in: path
      responses:
        "200":
          description: App secrets
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/AppSecrets"
                required:
                  - success
                  - data
        "400":
          description: App ID required
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/load-test:
    get:
      operationId: getTenantLoadTest
      tags:
        - Tenants
      summary: Get load test results for tenant
      description: Get load test status and results for the tenant. Returns default configuration if no test has been run.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Load test status
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/LoadTestResponse"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/dashboard/summary:
    get:
      operationId: getTenantDashboardSummary
      tags:
        - Tenants
      summary: Get dashboard summary for tenant
      description: Get aggregated dashboard metrics and summary data for the tenant including deployment status, app health, and recent activity.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Dashboard summary
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/DashboardSummary"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found
  /api/v1/tenants/{tenantId}/metrics/health:
    get:
      operationId: getTenantHealthMetrics
      tags:
        - Tenants
      summary: Get tenant health metrics time-series
      description: Get tenant health metrics as a time-series. Accepts ?range=24h|7d query parameter.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Health metrics time-series
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/TenantMetricsResponse"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/metrics/activity:
    get:
      operationId: getTenantActivityMetrics
      tags:
        - Tenants
      summary: Get tenant activity metrics time-series
      description: Get tenant deployment activity metrics as a time-series. Accepts ?range=24h|7d query parameter.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Activity metrics time-series
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/TenantMetricsResponse"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/metrics/resources:
    get:
      operationId: getTenantResourceMetrics
      tags:
        - Tenants
      summary: Get tenant resource usage metrics time-series
      description: Get tenant resource usage metrics (CPU, memory) as a time-series. Accepts ?range=24h|7d query parameter.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Resource metrics time-series
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/TenantMetricsResponse"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/security-alerts/events:
    get:
      operationId: listSecurityEvents
      tags:
        - Tenants
      summary: List security events for tenant
      description: List security events for the tenant. Supports query parameters for filtering and pagination.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Security events
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/SecurityEvent"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/security-alerts/metrics:
    get:
      operationId: getSecurityMetrics
      tags:
        - Tenants
      summary: Get security metrics for tenant
      description: Get aggregated security metrics (total events, open alerts, blocked entities) for the tenant.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Security metrics
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/SecurityMetrics"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/security-alerts/alerts:
    get:
      operationId: listTenantSecurityAlerts
      tags:
        - Tenants
      summary: List security alerts for tenant
      description: List security alerts for the tenant. Supports query parameters for filtering by status and severity.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Security alerts
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/SecurityAlert"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/security-alerts/alerts/{alertId}/acknowledge:
    post:
      operationId: acknowledgeSecurityAlert
      tags:
        - Tenants
      summary: Acknowledge a security alert
      description: Acknowledge a security alert. Transitions the alert from 'open' to 'acknowledged' status. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: alertId
          in: path
      responses:
        "200":
          description: Alert acknowledged
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/SecurityAlert"
                required:
                  - success
                  - data
        "400":
          description: Alert ID required
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/security-alerts/alerts/{alertId}/resolve:
    post:
      operationId: resolveSecurityAlert
      tags:
        - Tenants
      summary: Resolve a security alert
      description: Resolve a security alert. Transitions the alert to 'resolved' status. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: alertId
          in: path
      responses:
        "200":
          description: Alert resolved
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/SecurityAlert"
                required:
                  - success
                  - data
        "400":
          description: Alert ID required
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/security-alerts/rules:
    get:
      operationId: listTenantSecurityRules
      tags:
        - Tenants
      summary: List security rules for tenant
      description: List all security rules configured for the tenant.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Security rules
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/SecurityRule"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    post:
      operationId: createTenantSecurityRule
      tags:
        - Tenants
      summary: Create security rule for tenant
      description: Create a new security rule for the tenant. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: Rule name
                type:
                  type: string
                  description: Rule type
                enabled:
                  type: boolean
                  description: Whether rule is enabled
                conditions:
                  type: object
                  additionalProperties: {}
                  description: Rule conditions
                actions:
                  type: object
                  additionalProperties: {}
                  description: Rule actions
              required:
                - name
                - type
                - conditions
                - actions
      responses:
        "200":
          description: Rule created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/SecurityRule"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/security-alerts/rules/{ruleId}:
    put:
      operationId: updateTenantSecurityRule
      tags:
        - Tenants
      summary: Update security rule for tenant
      description: Update an existing security rule. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: ruleId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: Rule name
                type:
                  type: string
                  description: Rule type
                enabled:
                  type: boolean
                  description: Whether rule is enabled
                conditions:
                  type: object
                  additionalProperties: {}
                  description: Rule conditions
                actions:
                  type: object
                  additionalProperties: {}
                  description: Rule actions
      responses:
        "200":
          description: Rule updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/SecurityRule"
                required:
                  - success
                  - data
        "400":
          description: Rule ID required
        "401":
          description: Unauthorized
    delete:
      operationId: deleteTenantSecurityRule
      tags:
        - Tenants
      summary: Delete security rule for tenant
      description: Delete a security rule. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: ruleId
          in: path
      responses:
        "200":
          description: Rule deleted
        "400":
          description: Rule ID required
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/security-alerts/blocked:
    get:
      operationId: listBlockedEntities
      tags:
        - Tenants
      summary: List blocked entities for tenant
      description: List all blocked entities (IPs, users, devices) for the tenant.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Blocked entities
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/BlockedEntity"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    post:
      operationId: blockEntity
      tags:
        - Tenants
      summary: Block an entity for tenant
      description: Block an entity (IP, user, or device) for the tenant. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                entityType:
                  type: string
                  description: Entity type (ip, user, device)
                entityValue:
                  type: string
                  description: Entity value to block
                reason:
                  type: string
                  description: Reason for blocking
              required:
                - entityType
                - entityValue
      responses:
        "200":
          description: Entity blocked
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BlockedEntity"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/security-alerts/blocked/{blockId}:
    delete:
      operationId: unblockEntity
      tags:
        - Tenants
      summary: Unblock entity for tenant
      description: Remove a blocked entity for the tenant. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: blockId
          in: path
      responses:
        "200":
          description: Entity unblocked
        "400":
          description: Block ID required
        "401":
          description: Unauthorized
  /api/v1/hosts:
    get:
      operationId: listDevices
      tags:
        - Devices
      summary: List devices for tenant
      description: List all devices for the authenticated tenant with pagination. Sensitive fields (sharedSecret) are stripped from the response.
      responses:
        "200":
          description: Paginated device list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Device"
                  meta:
                    type: object
                    properties:
                      pagination:
                        $ref: "#/components/schemas/PaginationMeta"
                    required:
                      - pagination
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    post:
      operationId: createDevice
      tags:
        - Devices
      summary: Create new device
      description: Direct device creation is not supported. Use the pairing flow (POST /api/v1/pairing/initiate + POST /api/v1/pairing/activate) instead.
      responses:
        "400":
          description: Direct creation not supported - use pairing flow
  /api/v1/hosts/{deviceId}:
    get:
      operationId: getDevice
      tags:
        - Devices
      summary: Get device details
      description: Get detailed information for a specific device. Validates tenant ownership and strips sensitive fields.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      responses:
        "200":
          description: Device details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/Device"
                required:
                  - success
                  - data
        "400":
          description: Invalid device ID format
        "401":
          description: Unauthorized or device not found
        "403":
          description: Device does not belong to tenant
    delete:
      operationId: deleteDevice
      tags:
        - Devices
      summary: Delete device
      description: Delete a device from the tenant. Validates tenant ownership before deletion. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      responses:
        "200":
          description: Device deleted
        "400":
          description: Invalid device ID format
        "401":
          description: Unauthorized
        "403":
          description: Device does not belong to tenant
  /api/v1/hosts/{deviceId}/secrets:
    get:
      operationId: getDeviceSecrets
      tags:
        - Devices
      summary: Get device infrastructure secrets
      description: Retrieve infrastructure secrets for a device (TENANT_ID, DEVICE_TOKEN). API key auth only; portal session auth is blocked. Rate limited to 10 req/hour.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      responses:
        "200":
          description: Device secrets
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/DeviceSecretsResponse"
                required:
                  - success
                  - data
        "400":
          description: Invalid device ID
        "401":
          description: Unauthorized or device not found
        "429":
          description: Rate limit exceeded (10 req/hour)
  /api/v1/hosts/{deviceId}/rotate-secret:
    post:
      operationId: rotateDeviceSecret
      tags:
        - Devices
      summary: Rotate device secret
      description: Rotate the shared secret for a device. Supports grace period for zero-downtime rotation. Owner role required.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                gracePeriodMinutes:
                  type: number
                  description: Grace period in minutes during which both old and new secrets are accepted
                reason:
                  type: string
                  description: Reason for rotation (audit trail)
      responses:
        "200":
          description: Secret rotation initiated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/RotateSecretResponse"
                required:
                  - success
                  - data
        "400":
          description: Invalid device ID or request body
        "401":
          description: Unauthorized
  /api/v1/hosts/{deviceId}/volumes:
    get:
      operationId: listDeviceVolumes
      tags:
        - Volumes
      summary: List volumes for device
      description: Discover and list Docker volumes on a device. Sends a volume discovery command to the device agent.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      responses:
        "200":
          description: Volume discovery result
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/VolumeDiscoveryResult"
                required:
                  - success
                  - data
        "400":
          description: Invalid device ID
        "401":
          description: Unauthorized
        "403":
          description: Device does not belong to tenant
  /api/v1/hosts/{deviceId}/volumes/refresh:
    post:
      operationId: refreshDeviceVolumes
      tags:
        - Volumes
      summary: Trigger volume discovery refresh
      description: Trigger an immediate volume discovery refresh on a device. Returns immediately; discovery runs asynchronously.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      responses:
        "200":
          description: Volume discovery triggered
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/VolumeRefreshResponse"
                required:
                  - success
                  - data
        "400":
          description: Invalid device ID
        "401":
          description: Unauthorized
        "403":
          description: Device does not belong to tenant
  /api/v1/apps:
    get:
      operationId: listApps
      tags:
        - Apps
      summary: List apps
      description: List available app templates annotated with per-tenant availability based on billing plan.
      responses:
        "200":
          description: App template list with availability annotations
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/AppTemplate"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    post:
      operationId: createApp
      tags:
        - Apps
      summary: Create new app
      description: Create a new app instance from a template. Requires owner or admin role.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAppRequest"
      responses:
        "201":
          description: App created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/App"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - insufficient plan or role
  /api/v1/apps/{appId}/snapshots:
    get:
      operationId: listAppSnapshots
      tags:
        - Apps
      summary: Get app snapshots
      description: List snapshots for a specific app.
      parameters:
        - schema:
            type: string
          required: true
          name: appId
          in: path
      responses:
        "200":
          description: App snapshots
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/AppSnapshot"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/apps/{appId}/rollback:
    post:
      operationId: rollbackApp
      tags:
        - Apps
      summary: Rollback app to snapshot
      description: Roll back an app to a previous snapshot. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: appId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RollbackRequest"
      responses:
        "200":
          description: Rollback initiated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
  /api/v1/apps/{appId}/secrets:
    get:
      operationId: getAppSecrets
      tags:
        - Apps
      summary: Get app secrets
      description: Retrieve secrets for a specific app instance.
      parameters:
        - schema:
            type: string
          required: true
          name: appId
          in: path
      responses:
        "200":
          description: App secrets
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/AppSecrets"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/apps/{appId}/secrets/rotate:
    post:
      operationId: rotateAppSecrets
      tags:
        - Apps
      summary: Rotate app secrets
      description: Rotate secrets for a specific app. Owner only.
      parameters:
        - schema:
            type: string
          required: true
          name: appId
          in: path
      responses:
        "200":
          description: Secrets rotated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/AppSecrets"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - owner only
  /api/v1/apps/{appId}:
    get:
      operationId: getApp
      tags:
        - Apps
      summary: Get app details
      description: Get details for a specific app instance.
      parameters:
        - schema:
            type: string
          required: true
          name: appId
          in: path
      responses:
        "200":
          description: App details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/App"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    delete:
      operationId: deleteApp
      tags:
        - Apps
      summary: Delete app
      description: Delete an app instance. Supports ?preserveVolumes=true|false. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: appId
          in: path
      responses:
        "200":
          description: App deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
    patch:
      operationId: updateApp
      tags:
        - Apps
      summary: Update app (PATCH)
      description: Partially update an app instance. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: appId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateAppRequest"
      responses:
        "200":
          description: App updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/App"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
    put:
      operationId: replaceApp
      tags:
        - Apps
      summary: Update app (PUT)
      description: Fully replace an app instance configuration. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: appId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateAppRequest"
      responses:
        "200":
          description: App updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/App"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
  /api/v1/tenants/{tenantId}/exports:
    post:
      operationId: createExport
      tags:
        - Data Export
      summary: Create data export job
      description: Create a new data export job. The export runs in the background; poll the job status endpoint for completion.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateExportRequest"
      responses:
        "202":
          description: Export job created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  job:
                    $ref: "#/components/schemas/ExportJob"
                  message:
                    type: string
                  timestamp:
                    type: string
                required:
                  - success
                  - job
                  - message
                  - timestamp
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
    get:
      operationId: listExports
      tags:
        - Data Export
      summary: List data export jobs
      description: List export jobs for the tenant with pagination support.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Export job list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  jobs:
                    type: array
                    items:
                      $ref: "#/components/schemas/ExportJob"
                  count:
                    type: number
                  pagination:
                    type: object
                    properties:
                      limit:
                        type: number
                      offset:
                        type: number
                    required:
                      - limit
                      - offset
                  timestamp:
                    type: string
                required:
                  - success
                  - jobs
                  - count
                  - pagination
                  - timestamp
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/exports/{jobId}:
    get:
      operationId: getExport
      tags:
        - Data Export
      summary: Get export job status
      description: Get the status and details of a specific export job.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: jobId
          in: path
      responses:
        "200":
          description: Export job details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  job:
                    $ref: "#/components/schemas/ExportJob"
                  timestamp:
                    type: string
                required:
                  - success
                  - job
                  - timestamp
        "401":
          description: Unauthorized
        "404":
          description: Export job not found
  /api/v1/tenants/{tenantId}/exports/{jobId}/download:
    get:
      operationId: downloadExport
      tags:
        - Data Export
      summary: Download export file
      description: Download the completed export file. Only available for completed exports that have not expired.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: jobId
          in: path
      responses:
        "200":
          description: Export file binary
        "400":
          description: Export not ready for download
        "401":
          description: Unauthorized
        "404":
          description: Export job or file not found
        "410":
          description: Export has expired
  /api/v1/tenants/{tenantId}/imports:
    post:
      operationId: createImport
      tags:
        - Data Export
      summary: Create data import job
      description: Create a new data import job. The import runs in the background. Supports dry-run mode for validation.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateImportRequest"
      responses:
        "202":
          description: Import job created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  job:
                    $ref: "#/components/schemas/ImportJob"
                  message:
                    type: string
                  timestamp:
                    type: string
                required:
                  - success
                  - job
                  - message
                  - timestamp
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
    get:
      operationId: listImports
      tags:
        - Data Export
      summary: List data import jobs
      description: List import jobs for the tenant with pagination support.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Import job list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  jobs:
                    type: array
                    items:
                      $ref: "#/components/schemas/ImportJob"
                  count:
                    type: number
                  pagination:
                    type: object
                    properties:
                      limit:
                        type: number
                      offset:
                        type: number
                    required:
                      - limit
                      - offset
                  timestamp:
                    type: string
                required:
                  - success
                  - jobs
                  - count
                  - pagination
                  - timestamp
        "401":
          description: Unauthorized
  /api/v1/tenants/{tenantId}/imports/{jobId}:
    get:
      operationId: getImport
      tags:
        - Data Export
      summary: Get import job status
      description: Get the status and details of a specific import job.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: jobId
          in: path
      responses:
        "200":
          description: Import job details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  job:
                    $ref: "#/components/schemas/ImportJob"
                  timestamp:
                    type: string
                required:
                  - success
                  - job
                  - timestamp
        "401":
          description: Unauthorized
        "404":
          description: Import job not found
  /api/v1/tenants/{tenantId}/imports/upload:
    post:
      operationId: uploadImportFile
      tags:
        - Data Export
      summary: Upload import file
      description: Upload an import file to R2 storage. Accepts JSON, CSV, YAML, or multipart form data. Maximum 100MB.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: File uploaded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadImportFileResponse"
        "400":
          description: Invalid content type
        "401":
          description: Unauthorized
        "413":
          description: File too large
  /api/v1/account/deletion:
    post:
      operationId: requestAccountDeletion
      tags:
        - Account
      summary: Request account deletion (GDPR right to erasure)
      description: Schedule account for deletion after a 30-day grace period. Requires email confirmation matching the tenant owner email.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeletionRequest"
      responses:
        "200":
          description: Deletion scheduled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletionRequestResponse"
        "400":
          description: Invalid request or email mismatch
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found
        "409":
          description: Deletion already scheduled
  /api/v1/account/cancel-deletion:
    post:
      operationId: cancelAccountDeletion
      tags:
        - Account
      summary: Cancel pending account deletion
      description: Cancel a pending account deletion within the grace period.
      responses:
        "200":
          description: Deletion cancelled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletionCancelResponse"
        "400":
          description: No deletion scheduled
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found
        "410":
          description: Grace period expired
  /api/v1/account/deletion-status:
    get:
      operationId: getAccountDeletionStatus
      tags:
        - Account
      summary: Get account deletion status
      description: Get the current deletion status for the authenticated user's account, including days remaining in grace period.
      responses:
        "200":
          description: Deletion status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletionStatus"
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found
  /api/v1/account/export:
    post:
      operationId: requestAccountDataExport
      tags:
        - Account
      summary: Request a GDPR Article 15 data export
      description: |
        Queue a GDPR Article 15 data export. The orchestrator builds a JSONL
        archive of every D1 row referencing the authenticated tenant, encrypts
        it with AES-256-GCM (KEK from BACKUP_ENCRYPTION_KEY, AAD bound to
        tenantId+jobId), and uploads it to R2. Wave 2 (Phase 302-02) emails
        the customer a single-use 24h-expiry signed download URL.
        Rate-limited to 1 active job per tenant; subsequent requests within
        24h of a delivered job return 429 with `recentJobId`.
      responses:
        "202":
          description: Job queued
        "401":
          description: Unauthorized
        "429":
          description: Rate limit — another job is in flight or recently delivered
        "500":
          description: Failed to create export job
        "503":
          description: Export pipeline unavailable
  /api/v1/account/export/{jobId}/status:
    get:
      operationId: getAccountDataExportStatus
      tags:
        - Account
      summary: Get a GDPR data export job status
      description: |
        Returns the export job's state machine status, progress timestamps,
        archive size (when delivered), and download metadata. Cross-tenant
        access (jobId belongs to a different tenant) returns 403.
      parameters:
        - in: path
          name: jobId
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Job status
          content:
            application/json:
              schema:
                type: object
                required:
                  - jobId
                  - status
                  - requestedAt
                  - downloadCount
                properties:
                  jobId:
                    type: string
                    description: Unique job identifier (e.g. `gdpr-export-<uuid>`).
                  status:
                    type: string
                    description: Export pipeline state.
                    enum:
                      - queued
                      - running
                      - encrypting
                      - uploading
                      - delivered
                      - failed
                      - stuck
                      - expired
                  requestedAt:
                    type: string
                    format: date-time
                  startedAt:
                    type: string
                    format: date-time
                    nullable: true
                  deliveredAt:
                    type: string
                    format: date-time
                    nullable: true
                  exportSizeBytes:
                    type: integer
                    nullable: true
                    description: Size of the encrypted archive in R2 (only set when status=delivered).
                  downloadCount:
                    type: integer
                    description: How many times the signed URL has been redeemed (single-use; 1 = consumed).
                  errorClass:
                    type: string
                    nullable: true
                    description: Coarse-grained error category (e.g. `dispatch_failed`, `partial_export`).
                  downloadUrl:
                    type: string
                    nullable: true
                    description: |
                      Wave-2 (Phase 302-02) field: signed-URL redirect when status=delivered.
                      Stable schema placeholder — currently always null in Wave-1.
        "401":
          description: Unauthorized
        "403":
          description: Job belongs to a different tenant
        "404":
          description: Job not found
  /api/v1/account/recovery/contact:
    get:
      operationId: getAccountRecoveryContact
      tags:
        - Account
      summary: Get the current recovery contact email (authenticated)
      description: |
        Return the current recovery email (masked) and the timestamp it was last
        set. The recovery email is used to regain access if the tenant owner
        loses access to their primary login. Issue #5325.
      responses:
        "200":
          description: Recovery contact state
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found
    post:
      operationId: setAccountRecoveryContact
      tags:
        - Account
      summary: Set or update the recovery contact email (authenticated)
      description: |
        Designate an email address that can be used to recover access to the
        tenant if the owner loses their primary login. The recovery email must
        be different from the owner email. Both the previous recovery contact
        and the current owner are notified of the change. Issue #5325.
      responses:
        "200":
          description: Recovery contact updated
        "400":
          description: Invalid email or matches owner email
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found
    delete:
      operationId: clearAccountRecoveryContact
      tags:
        - Account
      summary: Clear the recovery contact email (authenticated)
      description: |
        Remove the recovery contact email. Recovery requests will no longer be
        deliverable until a new contact is set. Issue #5325.
      responses:
        "200":
          description: Recovery contact cleared
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found
  /api/v1/account/recovery/request:
    post:
      operationId: requestAccountRecovery
      tags:
        - Account
      summary: Initiate account recovery (public, unauthenticated)
      description: |
        Public endpoint — the caller is, by definition, locked out of their
        tenant. Accepts a tenant slug and a recovery email. If both match a
        live tenant's designated recovery contact, a single-use 1-hour recovery
        token is emailed to the recovery contact and a fraud-detection notice
        is sent to the current owner.

        The response is identical regardless of match status to prevent
        tenant/email enumeration. Issue #5325.
      responses:
        "200":
          description: Recovery initiated (generic success; may indicate match, no-match, rate-limit, or internal error)
        "400":
          description: Malformed request body
        "429":
          description: IP-level rate limit exceeded
  /api/v1/account/recovery/verify:
    post:
      operationId: verifyAccountRecoveryToken
      tags:
        - Account
      summary: Verify a recovery token and issue a 15-minute rebind session (public)
      description: |
        Public endpoint. Validates a recovery token (active, not expired,
        not consumed) and atomically consumes it. On success returns a
        15-minute rebind-session-id used by /rebind. Replay attempts return
        409 + RECOVERY_TOKEN_CONSUMED. Issue #5431.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - token
              properties:
                token:
                  type: string
                  minLength: 8
                  maxLength: 256
                  description: Recovery token delivered to the recovery contact email.
      responses:
        "200":
          description: Token verified, rebind session issued
        "400":
          description: Token invalid
        "403":
          description: Tenant not eligible for recovery
        "409":
          description: Token already consumed (replay)
        "410":
          description: Token expired
        "429":
          description: IP-level rate limit exceeded
  /api/v1/account/recovery/rebind:
    post:
      operationId: rebindAccountIdentity
      tags:
        - Account
      summary: Complete the recovery rebind — identity proof + billing re-verify (public)
      description: |
        Public endpoint. Consumes a rebind-session-id, verifies the identity
        proof (TOTP for owners with MFA, or email-fallback code), verifies
        billing via Paddle customer-match (or admin-approved manual_review),
        and atomically rebinds the tenant ownership to the new identity.
        Issue #5431.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - rebindSessionId
                - newIdentity
                - identityProof
              properties:
                rebindSessionId:
                  type: string
                  format: uuid
                  description: Session id returned by /verify; valid for 15 minutes.
                newIdentity:
                  type: object
                  required:
                    - email
                  properties:
                    email:
                      type: string
                      format: email
                      maxLength: 320
                identityProof:
                  type: object
                  required:
                    - method
                    - code
                  properties:
                    method:
                      type: string
                      enum: [totp, email]
                      description: |
                        Identity proof channel. `totp` validates the supplied
                        6-12 digit code against the current owner's MFA secret
                        and is fully supported by the orchestrator slice
                        (#5431). `method=email` is **provisional in this
                        slice**: the orchestrator accepts the trailing 6 hex
                        chars of the recovery token's hash as the fallback
                        code, but the portal cannot derive that value
                        client-side. Use `totp` for end-to-end flows until
                        Task 280-02b ships a server-issued OTP for this
                        path.
                    code:
                      type: string
                      minLength: 6
                      maxLength: 12
                tenantId:
                  type: string
                  minLength: 1
                  maxLength: 64
                  description: |
                    Optional. The server-side session.tenant_id is authoritative;
                    if supplied, must match or the request fails with
                    RECOVERY_TENANT_MISMATCH.
      responses:
        "200":
          description: Rebind completed
        "401":
          description: Identity proof failed
        "402":
          description: Billing not verified
        "403":
          description: Tenant mismatch / inactive
        "410":
          description: Rebind session expired or rejected
        "429":
          description: IP-level rate limit exceeded
        "502":
          description: Paddle billing-verify provider unavailable
  /api/v1/admin/recovery/pending:
    get:
      operationId: adminListPendingRebinds
      tags:
        - Admin
        - Account
      summary: List pending manual_review rebind sessions (admin)
      description: |
        Admin-only. Returns un-approved, un-rejected, un-expired rebind sessions
        for tenants whose billing_verification_method is 'manual_review'.
        Issue #5431.
      responses:
        "200":
          description: Pending sessions
        "401":
          description: Unauthorized
  /api/v1/admin/recovery/action:
    post:
      operationId: adminRecoveryAction
      tags:
        - Admin
        - Account
      summary: Approve or reject a manual_review rebind session (admin)
      description: |
        Admin-only. Approves a manual_review rebind session (allowing /rebind
        to succeed) or rejects it (terminating the session and emitting
        rebind_rejected_by_admin). Issue #5431.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - rebindSessionId
                - action
              properties:
                rebindSessionId:
                  type: string
                  format: uuid
                action:
                  type: string
                  enum: [approve, reject]
                notes:
                  type: string
                  maxLength: 2000
                  description: Optional free-form notes captured in the audit row.
      responses:
        "200":
          description: Decision recorded
        "401":
          description: Unauthorized
        "404":
          description: Session not found
        "409":
          description: Session already decided OR tenant not in manual_review
  /api/v1/webhooks:
    get:
      operationId: listWebhooks
      tags:
        - Webhooks
      summary: List all webhooks for tenant
      description: List all webhook subscriptions for the authenticated tenant.
      responses:
        "200":
          description: Webhook list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/WebhookSubscription"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    post:
      operationId: createWebhook
      tags:
        - Webhooks
      summary: Create a new webhook subscription
      description: Create a new webhook subscription for event notifications.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWebhookRequest"
      responses:
        "201":
          description: Webhook created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/WebhookSubscription"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
  /api/v1/webhooks/{webhookId}:
    get:
      operationId: getWebhook
      tags:
        - Webhooks
      summary: Get webhook details
      description: Get details of a specific webhook subscription.
      parameters:
        - schema:
            type: string
          required: true
          name: webhookId
          in: path
      responses:
        "200":
          description: Webhook details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/WebhookSubscription"
                required:
                  - success
                  - data
        "400":
          description: Invalid webhook ID format
        "401":
          description: Unauthorized
        "404":
          description: Webhook not found
    put:
      operationId: updateWebhook
      tags:
        - Webhooks
      summary: Update webhook subscription
      description: Update an existing webhook subscription.
      parameters:
        - schema:
            type: string
          required: true
          name: webhookId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateWebhookRequest"
      responses:
        "200":
          description: Webhook updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/WebhookSubscription"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
        "404":
          description: Webhook not found
    delete:
      operationId: deleteWebhook
      tags:
        - Webhooks
      summary: Delete webhook subscription
      description: Delete a webhook subscription.
      parameters:
        - schema:
            type: string
          required: true
          name: webhookId
          in: path
      responses:
        "200":
          description: Webhook deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "400":
          description: Invalid webhook ID format
        "401":
          description: Unauthorized
        "404":
          description: Webhook not found
  /api/v1/webhooks/{webhookId}/test:
    post:
      operationId: testWebhook
      tags:
        - Webhooks
      summary: Send test webhook notification
      description: Send a test notification to verify webhook delivery.
      parameters:
        - schema:
            type: string
          required: true
          name: webhookId
          in: path
      responses:
        "200":
          description: Test result
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/WebhookTestResult"
                required:
                  - success
                  - data
        "400":
          description: Invalid webhook ID format
        "401":
          description: Unauthorized
        "404":
          description: Webhook not found
  /api/v1/webhooks/{webhookId}/deliveries:
    get:
      operationId: getWebhookDeliveries
      tags:
        - Webhooks
      summary: Get webhook delivery history
      description: Get delivery history for a specific webhook.
      parameters:
        - schema:
            type: string
          required: true
          name: webhookId
          in: path
      responses:
        "200":
          description: Delivery history
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/WebhookDelivery"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Webhook not found
  /api/v1/webhooks/{webhookId}/stats:
    get:
      operationId: getWebhookStats
      tags:
        - Webhooks
      summary: Get webhook statistics
      description: Get delivery statistics for a specific webhook.
      parameters:
        - schema:
            type: string
          required: true
          name: webhookId
          in: path
      responses:
        "200":
          description: Webhook statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/WebhookStats"
                required:
                  - success
                  - data
        "400":
          description: Invalid webhook ID format
        "401":
          description: Unauthorized
        "404":
          description: Webhook not found
  /api/v1/tenants/{tenantId}/domains:
    get:
      operationId: listCustomDomains
      tags:
        - Custom Domains
      summary: List custom domains
      description: List all custom domains configured for the tenant.
      security:
        - BearerAuth: []
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Domain list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      domains:
                        type: array
                        items:
                          $ref: "#/components/schemas/CustomDomain"
                    required:
                      - domains
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    post:
      operationId: addCustomDomain
      tags:
        - Custom Domains
      summary: Add custom domain
      description: Add a new custom domain for the tenant. Requires Pro or Business billing plan.
      security:
        - BearerAuth: []
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AddDomainRequest"
      responses:
        "201":
          description: Domain created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/AddDomainResponse"
                required:
                  - success
                  - data
        "400":
          description: Invalid domain
        "401":
          description: Unauthorized
        "409":
          description: Domain already registered
        "422":
          description: App not found
  /api/v1/tenants/{tenantId}/domains/{domainId}/verify:
    post:
      operationId: verifyCustomDomain
      tags:
        - Custom Domains
      summary: Verify custom domain
      description: Initiate DNS/SSL verification by creating a Cloudflare for SaaS custom hostname. Requires CNAME record to be configured first.
      security:
        - BearerAuth: []
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: domainId
          in: path
      responses:
        "200":
          description: Verification initiated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/VerifyDomainResponse"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Domain not found
        "422":
          description: CNAME not configured or no tunnel
  /api/v1/tenants/{tenantId}/domains/{domainId}/status:
    get:
      operationId: getCustomDomainStatus
      tags:
        - Custom Domains
      summary: Check custom domain status
      description: Check the current SSL/hostname verification status for a custom domain. Queries Cloudflare for live status.
      security:
        - BearerAuth: []
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: domainId
          in: path
      responses:
        "200":
          description: Domain status
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/CustomDomain"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Domain not found
  /api/v1/tenants/{tenantId}/domains/{domainId}:
    delete:
      operationId: removeCustomDomain
      tags:
        - Custom Domains
      summary: Remove custom domain
      description: Remove a custom domain and clean up the Cloudflare custom hostname.
      security:
        - BearerAuth: []
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
        - schema:
            type: string
          required: true
          name: domainId
          in: path
      responses:
        "204":
          description: Domain removed
        "401":
          description: Unauthorized
        "404":
          description: Domain not found
  /api/v1/hosts/{deviceId}/tunnels:
    post:
      operationId: createDeviceTunnel
      tags:
        - Tunnels
      summary: Create device tunnel
      description: Create a Cloudflare tunnel for a specific device. Requires HMAC auth.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateDeviceTunnelRequest"
      responses:
        "201":
          description: Tunnel created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/Tunnel"
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized - HMAC verification failed
    get:
      operationId: listDeviceTunnels
      tags:
        - Tunnels
      summary: List device tunnels
      description: List all tunnels for a specific device. Requires HMAC auth.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
      responses:
        "200":
          description: Tunnel list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Tunnel"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/hosts/{deviceId}/tunnels/{tunnelId}/credentials:
    get:
      operationId: getTunnelCredentials
      tags:
        - Tunnels
      summary: Get tunnel credentials
      description: Retrieve Cloudflare tunnel credentials (token) for a device tunnel.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
        - schema:
            type: string
          required: true
          name: tunnelId
          in: path
      responses:
        "200":
          description: Tunnel credentials
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/TunnelCredentials"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Tunnel not found
  /api/v1/hosts/{deviceId}/tunnels/{tunnelId}:
    put:
      operationId: updateDeviceTunnel
      tags:
        - Tunnels
      summary: Update device tunnel
      description: Update a device tunnel configuration.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
        - schema:
            type: string
          required: true
          name: tunnelId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateTunnelRequest"
      responses:
        "200":
          description: Tunnel updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/Tunnel"
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
    delete:
      operationId: deleteDeviceTunnel
      tags:
        - Tunnels
      summary: Delete device tunnel
      description: Delete a device tunnel and clean up Cloudflare resources.
      parameters:
        - schema:
            type: string
          required: true
          name: deviceId
          in: path
        - schema:
            type: string
          required: true
          name: tunnelId
          in: path
      responses:
        "200":
          description: Tunnel deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Tunnel not found
  /api/v1/tunnels:
    post:
      operationId: createGlobalTunnel
      tags:
        - Tunnels
      summary: Create global tunnel
      description: Create a global (non-device-specific) tunnel.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateGlobalTunnelRequest"
      responses:
        "201":
          description: Global tunnel created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/Tunnel"
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
  /api/v1/tunnels/{tunnelId}/health:
    get:
      operationId: getTunnelHealth
      tags:
        - Tunnels
      summary: Get tunnel health
      description: Get health status for a specific tunnel.
      security: []
      parameters:
        - schema:
            type: string
          required: true
          name: tunnelId
          in: path
      responses:
        "200":
          description: Tunnel health status
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/TunnelHealth"
                required:
                  - success
                  - data
        "404":
          description: Tunnel not found
  /api/v1/tunnels/{tunnelId}/stop:
    post:
      operationId: stopTunnel
      tags:
        - Tunnels
      summary: Stop tunnel
      description: Stop an active tunnel without deleting it.
      parameters:
        - schema:
            type: string
          required: true
          name: tunnelId
          in: path
      responses:
        "200":
          description: Tunnel stopped
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "404":
          description: Tunnel not found
  /api/v1/tunnels/{tunnelId}:
    delete:
      operationId: deleteGlobalTunnel
      tags:
        - Tunnels
      summary: Delete global tunnel
      description: Delete a global tunnel and clean up Cloudflare resources.
      parameters:
        - schema:
            type: string
          required: true
          name: tunnelId
          in: path
      responses:
        "200":
          description: Tunnel deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "404":
          description: Tunnel not found
  /api/v1/billing/paddle/webhook:
    post:
      operationId: handlePaddleWebhook
      tags:
        - Billing
      summary: Paddle webhook handler
      description: Receive and process Paddle webhook events (subscription.created, updated, canceled, etc.). Authenticated via Paddle signature header.
      responses:
        "200":
          description: Webhook processed successfully
        "400":
          description: Invalid webhook payload
        "401":
          description: Invalid Paddle signature
  /api/v1/billing/paddle/checkout:
    post:
      operationId: createCheckout
      tags:
        - Billing
      summary: Create Paddle checkout session (owner only)
      description: Create a Paddle checkout session for plan upgrade. Owner only.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CheckoutRequest"
      responses:
        "200":
          description: Checkout session created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/CheckoutResponse"
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - owner only
  /api/v1/billing/paddle/cancel:
    post:
      operationId: cancelSubscription
      tags:
        - Billing
      summary: Cancel subscription (owner only)
      description: Cancel an active Paddle subscription. Can be immediate or at end of billing period. Owner only.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CancelSubscriptionRequest"
      responses:
        "200":
          description: Cancellation confirmed
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - owner only
  /api/v1/billing/paddle/change-plan:
    post:
      operationId: changePlan
      tags:
        - Billing
      summary: Change subscription plan (owner only)
      description: Change the current subscription plan (upgrade or downgrade). Owner only.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChangePlanRequest"
      responses:
        "200":
          description: Plan change processed
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - owner only
  /api/v1/billing/paddle/portal-session:
    post:
      operationId: createPortalSession
      tags:
        - Billing
      summary: Create Paddle customer portal session (owner only)
      description: Create a Paddle customer portal session URL for managing payment methods and invoices. Owner only.
      responses:
        "200":
          description: Portal session created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      url:
                        type: string
                        description: Portal session URL
                    required:
                      - url
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - owner only
  /api/v1/billing/plans:
    get:
      operationId: getPlans
      tags:
        - Billing
      summary: Get available billing plans (public)
      description: Get all available billing plans with pricing and feature details. Public endpoint.
      responses:
        "200":
          description: Available billing plans
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BillingPlansResponse"
                required:
                  - success
                  - data
  /api/v1/tenants/{tenantId}/billing:
    get:
      operationId: getTenantBilling
      tags:
        - Billing
      summary: Get tenant billing information
      description: Get billing information, subscription status, and usage for a specific tenant.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Tenant billing details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/TenantBilling"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found
  /api/v1/tenants/{tenantId}/billing/plan:
    put:
      operationId: updateTenantPlan
      tags:
        - Billing
      summary: Update tenant billing plan
      description: Directly update a tenant's billing plan (admin operation). Bypasses Paddle.
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateTenantPlanRequest"
      responses:
        "200":
          description: Plan updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - owner only
        "404":
          description: Tenant not found
  /api/v1/billing/webhook-health:
    get:
      operationId: getBillingWebhookHealth
      tags:
        - Billing
      summary: Billing webhook configuration health check
      description: Check health of billing webhook configuration.
      responses:
        "200":
          description: Webhook health status
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/WebhookHealth"
                required:
                  - success
                  - data
  /api/v1/fpb/invite/accept:
    post:
      operationId: acceptFpbInvite
      tags:
        - Billing
      summary: Accept a first-paid-beta invite (public, token-authenticated)
      description: |
        Phase 311-02 / FPB-311-D6+D2. Validates an HMAC-signed invite token and the
        4 legal-doc sha256s the user clicked through (TOS + Privacy + DPA + Cookies).
        Creates a placeholder tenant row and writes an audit row to invite_acceptances.
        One-time use enforced via email_sha256 PK.
      responses:
        "201":
          description: Invite accepted; tenant placeholder created
        "400":
          description: Acceptance shas do not match current legal sentinel
        "401":
          description: Invite token invalid or expired
        "409":
          description: Invite already accepted (one-time use)
        "503":
          description: Legal sentinel absent or signing secret unset
  /api/v1/admin/fpb/invite:
    post:
      operationId: sendFpbInvite
      tags:
        - Billing
      summary: Send a first-paid-beta invite (operator-only, bearer-authenticated)
      description: |
        Phase 311-02 / FPB-311-G4. Operator-only endpoint, bearer-authenticated against
        ORCHESTRATOR_API_KEY (constant-time compare; same shape as synthetic-tenant
        admin routes). Signs an HMAC token for the target email and dispatches the
        personal-note invite via SES.
      responses:
        "200":
          description: Invite sent
        "401":
          description: Unauthorized
        "502":
          description: Email provider failed
        "503":
          description: FPB signing secret unset or no email provider configured
  /api/v1/onboarding/sla/acknowledge:
    post:
      operationId: acknowledgeSlaDisclosure
      tags:
        - Onboarding
      summary: Acknowledge SLA disclosure (server-side persistence; Phase 309 follow-up)
      description: |
        Phase 309 server-side persistence follow-up. Persists a tenant's
        acknowledgement of the current SLA disclosure copy. Writes
        `tenants.sla_acknowledged_at` + `tenants.sla_disclosure_sha256`.
        Idempotent on (tenant_id, disclosure_sha256) — re-acks with the same
        sha return 200 + `already_acknowledged: true` without rewriting the
        timestamp. The incoming sha must appear in the server-side disclosure
        manifest (defends T-309-11 modal client-bypass).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - disclosure_sha256
              properties:
                disclosure_sha256:
                  type: string
                  pattern: "^[0-9a-f]{64}$"
                  description: |
                    Lowercase-hex sha256 of the exact disclosure copy the
                    tenant clicked through. Must appear in the server-side
                    `KNOWN_SLA_DISCLOSURE_SHAS` manifest.
                disclosure_version:
                  type: string
                  minLength: 1
                  maxLength: 64
                  description: |
                    Optional client-claimed disclosure-version identifier.
                    Logged for forensics; the server's notion of the current
                    version (derived from the sha) is authoritative.
      responses:
        "200":
          description: Acknowledgement persisted (or already present)
          content:
            application/json:
              schema:
                type: object
                required:
                  - acknowledged
                  - already_acknowledged
                  - acknowledged_at
                  - disclosure_sha256
                  - disclosure_version
                properties:
                  acknowledged:
                    type: boolean
                  already_acknowledged:
                    type: boolean
                    description: True when an existing row with the same sha was found and the timestamp was preserved.
                  acknowledged_at:
                    type: string
                    format: date-time
                  disclosure_sha256:
                    type: string
                    pattern: "^[0-9a-f]{64}$"
                  disclosure_version:
                    type: string
                    nullable: true
                    description: |
                      The server's current version label when the stored sha
                      matches `CURRENT_SLA_DISCLOSURE_SHA256`; `null` on an
                      idempotent re-ack of an OLD grace-window sha (the row
                      was acked under a prior revision).
        "400":
          description: Invalid disclosure_sha256 (bad format or not in manifest)
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found (including a 0-row UPDATE on a race against tenant deletion)
        "503":
          description: Database unavailable
  /api/v1/deployments:
    get:
      operationId: listDeployments
      tags:
        - Deployments
      summary: List deployments
      description: List all deployments for the authenticated tenant with pagination.
      responses:
        "200":
          description: Paginated deployment list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Deployment"
                  meta:
                    type: object
                    properties:
                      pagination:
                        $ref: "#/components/schemas/PaginationMeta"
                    required:
                      - pagination
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    post:
      operationId: createDeployment
      tags:
        - Deployments
      summary: Create new deployment
      description: Create a new deployment. Checks billing entitlements and resource limits. Runtime (docker-compose vs k3s) is determined by billing tier.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateDeploymentRequest"
      responses:
        "200":
          description: Deployment created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/Deployment"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "403":
          description: Tenant ID mismatch or entitlement denied
        "409":
          description: Idempotent duplicate
  /api/v1/deployments/{deploymentId}:
    get:
      operationId: getDeployment
      tags:
        - Deployments
      summary: Get deployment details
      description: Get detailed information for a specific deployment. Validates tenant ownership.
      parameters:
        - schema:
            type: string
          required: true
          name: deploymentId
          in: path
      responses:
        "200":
          description: Deployment details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/Deployment"
                required:
                  - success
                  - data
        "400":
          description: Invalid deployment ID format
        "401":
          description: Unauthorized or deployment not found
        "403":
          description: Deployment does not belong to tenant
    delete:
      operationId: deleteDeployment
      tags:
        - Deployments
      summary: Delete deployment
      description: Delete a deployment and clean up associated OIDC clients. Validates tenant ownership. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: deploymentId
          in: path
      responses:
        "200":
          description: Deployment deleted
        "400":
          description: Invalid deployment ID format
        "401":
          description: Unauthorized
        "403":
          description: Deployment does not belong to tenant
    patch:
      operationId: updateDeployment
      tags:
        - Deployments
      summary: Update deployment
      description: Update a deployment's configuration. Validates tenant ownership. Idempotent via request idempotency key.
      parameters:
        - schema:
            type: string
          required: true
          name: deploymentId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateDeploymentRequest"
      responses:
        "200":
          description: Deployment updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/Deployment"
                required:
                  - success
                  - data
        "400":
          description: Invalid deployment ID or request body
        "401":
          description: Unauthorized
        "403":
          description: Deployment does not belong to tenant
  /api/v1/deployments/{deploymentId}/export:
    get:
      operationId: exportDeploymentCompose
      tags:
        - Deployments
      summary: Export deployment as docker-compose.yml
      description: Export a deployment's configuration as a docker-compose.yml file. Secret values are redacted. Returns YAML content with Content-Disposition attachment header.
      parameters:
        - schema:
            type: string
          required: true
          name: deploymentId
          in: path
      responses:
        "200":
          description: Docker Compose YAML file (application/yaml)
        "400":
          description: Invalid deployment ID format
        "401":
          description: Unauthorized or deployment not found
        "403":
          description: Deployment does not belong to tenant
  /api/v1/deployments/{deploymentId}/dashboard:
    get:
      operationId: getDeploymentDashboard
      tags:
        - Deployments
      summary: Get dashboard for deployment
      description: Get Grafana dashboard metadata for a deployment (UID, title, tags, panel count). Does not return the full dashboard JSON.
      parameters:
        - schema:
            type: string
          required: true
          name: deploymentId
          in: path
      responses:
        "200":
          description: Dashboard metadata
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/DashboardMeta"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Deployment or dashboard not found
  /api/v1/deployments/{deploymentId}/dashboard/export:
    get:
      operationId: exportDeploymentDashboard
      tags:
        - Deployments
      summary: Export dashboard JSON for deployment
      description: Export the full Grafana dashboard JSON for a deployment. Can be imported into a standalone Grafana instance.
      parameters:
        - schema:
            type: string
          required: true
          name: deploymentId
          in: path
      responses:
        "200":
          description: Full dashboard JSON
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/DashboardExport"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Deployment or dashboard not found
  /api/v1/deployments/{deploymentId}/meshroute/provision:
    post:
      operationId: provisionMeshRoute
      tags:
        - Mesh Routes
      summary: Provision mesh route for deployment
      description: Provision a mesh route for a deployment. Creates Headscale node, DNS records, and Traefik ingress. Returns a public URL.
      parameters:
        - schema:
            type: string
          required: true
          name: deploymentId
          in: path
      responses:
        "201":
          description: Mesh route provisioning initiated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/MeshRouteProvisionResponse"
                required:
                  - success
                  - data
        "400":
          description: Invalid deployment ID or missing tenant slug
        "401":
          description: Unauthorized
        "403":
          description: MeshRoute feature disabled
        "404":
          description: Deployment or tenant not found
        "409":
          description: Mesh route already provisioned
  /api/v1/deployments/{deploymentId}/meshroute/status:
    get:
      operationId: getMeshRouteStatus
      tags:
        - Mesh Routes
      summary: Get mesh route status for deployment
      description: Get the current mesh route status for a deployment, including live Headscale node connection status.
      parameters:
        - schema:
            type: string
          required: true
          name: deploymentId
          in: path
      responses:
        "200":
          description: Mesh route status
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/MeshRouteStatus"
                required:
                  - success
                  - data
        "400":
          description: Invalid deployment ID
        "401":
          description: Unauthorized
        "404":
          description: Deployment not found
  /api/v1/deployments/{deploymentId}/meshroute:
    delete:
      operationId: deprovisionMeshRoute
      tags:
        - Mesh Routes
      summary: Deprovision mesh route for deployment
      description: Deprovision a mesh route, cleaning up Headscale node, DNS, ingress, and Vault secrets. Idempotent — returns success if already disabled.
      parameters:
        - schema:
            type: string
          required: true
          name: deploymentId
          in: path
      responses:
        "200":
          description: Mesh route deprovisioned
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    properties:
                      success:
                        type: boolean
                        enum:
                          - true
                    required:
                      - success
                required:
                  - success
                  - data
        "400":
          description: Invalid deployment ID
        "401":
          description: Unauthorized
        "404":
          description: Deployment not found
  /api/v1/deployments/{deploymentId}/custom-compose:
    post:
      operationId: deployCustomCompose
      tags:
        - Deployments
      summary: Deploy custom docker-compose file
      description: Deploy a user-provided docker-compose.yml file with security validation and hardening. Requires owner or admin role.
      parameters:
        - schema:
            type: string
          required: true
          name: deploymentId
          in: path
        - schema:
            type: string
          required: true
          name: deviceId
          in: query
          description: Target device ID
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                composeContent:
                  type: string
                  description: Raw docker-compose.yml content
              required:
                - composeContent
      responses:
        "200":
          description: Custom compose deployed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                required:
                  - success
                  - data
        "400":
          description: Invalid request or compose validation failed
        "401":
          description: Unauthorized
        "404":
          description: Deployment not found
  /api/v1/batch:
    post:
      operationId: createBatchOperation
      tags:
        - Batch
      summary: Create batch operation
      description: Create a batch operation to execute commands across multiple devices.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateBatchOperationRequest"
      responses:
        "201":
          description: Batch operation created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BatchOperation"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
    get:
      operationId: listBatchOperations
      tags:
        - Batch
      summary: List batch operations
      description: List all batch operations for the tenant.
      responses:
        "200":
          description: Batch operations list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/BatchOperation"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/batch/{operationId}:
    get:
      operationId: getBatchOperation
      tags:
        - Batch
      summary: Get batch operation status
      description: Get the status and results of a specific batch operation.
      parameters:
        - schema:
            type: string
          required: true
          name: operationId
          in: path
      responses:
        "200":
          description: Batch operation details
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BatchOperation"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Operation not found
  /api/v1/alerts:
    post:
      operationId: createAlertRule
      tags:
        - Batch
      summary: Create alert rule
      description: Create a new alert rule for monitoring device or app conditions.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAlertRuleRequest"
      responses:
        "201":
          description: Alert rule created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/AlertRule"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
    get:
      operationId: listAlertRules
      tags:
        - Batch
      summary: List alert rules
      description: List all alert rules for the tenant.
      responses:
        "200":
          description: Alert rules list
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/AlertRule"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/alert-summary:
    get:
      operationId: getAlertStats
      tags:
        - Batch
      summary: Get alert statistics summary
      description: Get alert statistics summary for the tenant.
      responses:
        "200":
          description: Alert statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/AlertStats"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/notification-channels:
    get:
      operationId: listNotificationChannels
      tags:
        - Batch
      summary: List notification channels
      description: List all notification channels for the tenant.
      responses:
        "200":
          description: Notification channels
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/NotificationChannel"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    post:
      operationId: createNotificationChannel
      tags:
        - Batch
      summary: Create notification channel
      description: Create a new notification channel (email, webhook, Slack, etc.).
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateNotificationChannelRequest"
      responses:
        "201":
          description: Channel created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/NotificationChannel"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
  /api/v1/notification-channels/{channelId}:
    delete:
      operationId: deleteNotificationChannel
      tags:
        - Batch
      summary: Delete notification channel
      description: Delete a notification channel.
      parameters:
        - schema:
            type: string
          required: true
          name: channelId
          in: path
      responses:
        "200":
          description: Channel deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Channel not found
  /api/v1/security-alerts:
    get:
      operationId: getSecurityAlerts
      tags:
        - Batch
      summary: Query security alert events
      description: Query security alert events for the tenant.
      responses:
        "200":
          description: Security alert events
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/SecurityAlert"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/security-alerts/stats:
    get:
      operationId: getSecurityAlertStats
      tags:
        - Batch
      summary: Get security alert statistics
      description: Get security alert statistics for the tenant.
      responses:
        "200":
          description: Security alert statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/AlertStats"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/security-alerts/alerts:
    get:
      operationId: listSecurityAlerts
      tags:
        - Batch
      summary: List active security alerts
      description: List active security alerts for the tenant.
      responses:
        "200":
          description: Active security alerts
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/SecurityAlert"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/security-alerts/rules:
    get:
      operationId: listSecurityRules
      tags:
        - Batch
      summary: List security rules
      description: List security rules configured for the tenant.
      responses:
        "200":
          description: Security rules
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/SecurityRule"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    post:
      operationId: createSecurityRule
      tags:
        - Batch
      summary: Create security rule
      description: Create a new security rule.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSecurityRuleRequest"
      responses:
        "201":
          description: Security rule created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/SecurityRule"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
  /api/v1/security-alerts/rules/{ruleId}:
    put:
      operationId: updateSecurityRule
      tags:
        - Batch
      summary: Update security rule
      description: Update an existing security rule.
      parameters:
        - schema:
            type: string
          required: true
          name: ruleId
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateSecurityRuleRequest"
      responses:
        "200":
          description: Security rule updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/SecurityRule"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
        "404":
          description: Rule not found
    delete:
      operationId: deleteSecurityRule
      tags:
        - Batch
      summary: Delete security rule
      description: Delete a security rule.
      parameters:
        - schema:
            type: string
          required: true
          name: ruleId
          in: path
      responses:
        "200":
          description: Security rule deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Rule not found
  /api/v1/security-alerts/blocked:
    get:
      operationId: listSecurityBlocked
      tags:
        - Batch
      summary: List blocked entities
      description: List all blocked entities (IPs, users, etc.) for the tenant.
      responses:
        "200":
          description: Blocked entities
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/SecurityBlocked"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
    post:
      operationId: createSecurityBlock
      tags:
        - Batch
      summary: Block an entity
      description: Block an entity (IP, user, etc.).
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSecurityBlockRequest"
      responses:
        "201":
          description: Entity blocked
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/SecurityBlocked"
                required:
                  - success
                  - data
        "400":
          description: Validation error
        "401":
          description: Unauthorized
  /api/v1/security-alerts/blocked/{blockId}:
    delete:
      operationId: deleteSecurityBlock
      tags:
        - Batch
      summary: Unblock an entity
      description: Remove a block on an entity.
      parameters:
        - schema:
            type: string
          required: true
          name: blockId
          in: path
      responses:
        "200":
          description: Entity unblocked
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Block not found
  /api/v1/backup-policies/{policyId}:
    delete:
      operationId: deleteBackupPolicy
      tags:
        - Backups
      summary: Delete backup policy
      description: Delete a backup policy.
      parameters:
        - schema:
            type: string
          required: true
          name: policyId
          in: path
      responses:
        "200":
          description: Backup policy deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: object
                    additionalProperties: {}
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
        "404":
          description: Policy not found
  /api/v1/health/alerts:
    get:
      operationId: getHealthAlerts
      tags:
        - Batch
      summary: Get health alerts
      description: Get health alerts for the tenant.
      responses:
        "200":
          description: Health alerts
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/HealthAlert"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/admin/db-stats:
    get:
      operationId: getDbStats
      tags:
        - General
      summary: Get database statistics
      description: Get D1 database statistics. Admin endpoint.
      responses:
        "200":
          description: Database statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/DbStats"
                required:
                  - success
                  - data
        "401":
          description: Unauthorized
  /api/v1/apps/bulk-create:
    post:
      operationId: bulkCreateApps
      tags:
        - Bulk Operations
      summary: Create multiple apps in bulk
      description: Create multiple apps in a single operation. Supports concurrency control and continue-on-error behavior.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BulkAppCreateRequest"
      responses:
        "200":
          description: Bulk operation results
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BulkOperationResult"
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found
  /api/v1/apps/bulk-update:
    post:
      operationId: bulkUpdateApps
      tags:
        - Bulk Operations
      summary: Update multiple apps in bulk
      description: Update multiple apps in a single operation with per-app change sets.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BulkAppUpdateRequest"
      responses:
        "200":
          description: Bulk operation results
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BulkOperationResult"
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found
  /api/v1/apps/bulk-delete:
    post:
      operationId: bulkDeleteApps
      tags:
        - Bulk Operations
      summary: Delete multiple apps in bulk
      description: Delete multiple apps in a single operation. Defaults to continue-on-error mode.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BulkAppDeleteRequest"
      responses:
        "200":
          description: Bulk operation results
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BulkOperationResult"
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found
  /api/v1/hosts/bulk-operations:
    post:
      operationId: bulkDeviceOperations
      tags:
        - Bulk Operations
      summary: Execute bulk operations on multiple devices
      description: Execute operations (restart, update_config, rotate_secrets, health_check, deploy_app, remove_app) across multiple devices.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BulkDeviceOperationRequest"
      responses:
        "200":
          description: Bulk operation results
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BulkOperationResult"
                required:
                  - success
                  - data
        "400":
          description: Invalid request
        "401":
          description: Unauthorized
        "404":
          description: Tenant not found
  /api/v1/tenants/{tenantId}/load-test/scenarios:
    get:
      operationId: apiV1TenantsLoadTestScenariosByTenantIdGet
      tags:
        - Tenants
      summary: Get load test scenarios (post-beta feature)
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/tenants/{tenantId}/api-performance:
    get:
      operationId: apiV1TenantsApiPerformanceByTenantIdGet
      tags:
        - Tenants
      summary: Get API performance metrics (post-beta feature)
      parameters:
        - schema:
            type: string
          required: true
          name: tenantId
          in: path
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/beta/signup:
    post:
      operationId: betaSignup
      tags:
        - General
      summary: Beta signup (public)
      description: Submit a beta waitlist signup. Public endpoint, rate limited to 3 per hour per IP.
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BetaSignupRequest"
      responses:
        "200":
          description: Already on waitlist
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BetaSignupResponse"
                required:
                  - success
                  - data
        "201":
          description: Successfully joined waitlist
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: "#/components/schemas/BetaSignupResponse"
                required:
                  - success
                  - data
        "400":
          description: Invalid email
        "429":
          description: Rate limit exceeded
    options:
      operationId: betaSignupPreflight
      tags:
        - General
      summary: Beta signup CORS preflight
      description: CORS preflight for beta signup endpoint.
      responses:
        "204":
          description: CORS preflight response
  /api/v1/app-templates:
    get:
      operationId: getAppTemplates
      tags:
        - General
      summary: Get app templates (public)
      description: Get available app templates. Public endpoint - returns static metadata only.
      responses:
        "200":
          description: App template catalog
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/AppTemplate"
  /api/portal/app-updates:
    get:
      operationId: apiLegacyPortalAppUpdatesGet
      tags:
        - General
      summary: Get available app updates for tenant
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/changelog:
    get:
      operationId: apiV1ChangelogGet
      tags:
        - General
      summary: Get platform changelog entries
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/tenants/{tenantId}/enrollments:
    post:
      operationId: apiV1TenantEnrollmentsPost
      tags:
        - Enrollments
      summary: Create enrollment
      parameters:
        - name: tenantId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/metrics/rum:
    post:
      operationId: apiV1MetricsRumPost
      tags:
        - Metrics
      summary: Record RUM metrics
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/tenants/{tenantId}/notification-preferences:
    get:
      operationId: apiV1TenantNotificationPreferencesGet
      tags:
        - Tenant
      summary: Get notification preferences
      parameters:
        - name: tenantId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
    put:
      operationId: apiV1TenantNotificationPreferencesPut
      tags:
        - Tenant
      summary: Update notification preferences
      parameters:
        - name: tenantId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/tenants/{tenantId}/incident-prefs:
    get:
      operationId: apiV1TenantIncidentPrefsGet
      tags:
        - Notifications
      summary: Get incident email opt-in toggles (P0/P1/P2/P3)
      description: |
        Get per-tenant incident email opt-in toggles. Defaults: P0/P1 ON, P2/P3 OFF.
        v1.44 Phase 304 / EMAIL-01.
      parameters:
        - name: tenantId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - tenant mismatch
    put:
      operationId: apiV1TenantIncidentPrefsPut
      tags:
        - Notifications
      summary: Update incident email opt-in toggles (P0/P1/P2/P3)
      description: |
        Update per-tenant incident email opt-in toggles.
        v1.44 Phase 304 / EMAIL-01.
      parameters:
        - name: tenantId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "403":
          description: Forbidden - tenant mismatch
  /api/v1/tenants/{tenantId}/support/tickets:
    post:
      operationId: apiV1TenantSupportTicketsPost
      tags:
        - Support
      summary: Create support ticket
      parameters:
        - name: tenantId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
    get:
      operationId: apiV1TenantSupportTicketsGet
      tags:
        - Support
      summary: List support tickets
      parameters:
        - name: tenantId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/tenants/{tenantId}/support/tickets/{ticketId}:
    get:
      operationId: apiV1TenantSupportTicketGet
      tags:
        - Support
      summary: Get support ticket
      parameters:
        - name: tenantId
          in: path
          required: true
          schema:
            type: string
        - name: ticketId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/tenants/{tenantId}/api-keys:
    post:
      operationId: apiV1TenantApiKeysPost
      tags:
        - API Keys
      summary: Create API key
      parameters:
        - name: tenantId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
    get:
      operationId: apiV1TenantApiKeysGet
      tags:
        - API Keys
      summary: List API keys
      parameters:
        - name: tenantId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/tenants/{tenantId}/api-keys/{keyId}:
    get:
      operationId: apiV1TenantApiKeyGet
      tags:
        - API Keys
      summary: Get API key
      parameters:
        - name: tenantId
          in: path
          required: true
          schema:
            type: string
        - name: keyId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
    delete:
      operationId: apiV1TenantApiKeyDelete
      tags:
        - API Keys
      summary: Delete API key
      parameters:
        - name: tenantId
          in: path
          required: true
          schema:
            type: string
        - name: keyId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api/v1/portal/app-updates:
    get:
      operationId: apiV1PortalAppUpdatesGet
      tags:
        - General
      summary: Get available app updates for tenant
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /:
    get:
      operationId: get
      tags:
        - General
      summary: API information
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
  /api:
    get:
      operationId: ApiGet
      tags:
        - General
      summary: API information
      responses:
        "200":
          description: Success
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
        "404":
          description: Not Found
        "500":
          description: Internal Server Error
security:
  - BearerAuth: []
