openapi: 3.0.3
info:
  title: FastGateway API
  description: API for managing Kubernetes Gateway API resources
  version: 1.0.0
  contact:
    name: FastGateway
servers:
  - url: http://localhost:8081/api/v1
    description: Local development server
tags:
  - name: Auth
    description: Authentication endpoints
  - name: Users
    description: User management
  - name: Projects
    description: Project (Kubernetes cluster) management
  - name: Teams
    description: Global team management
  - name: Project Teams
    description: Team-project role assignments
  - name: Domain Templates
    description: Domain template management
  - name: Domains
    description: Domain (Gateway) management
  - name: Project Namespaces
    description: Project namespace whitelist management
  - name: Routes
    description: Route (HTTPRoute/GRPCRoute) management
  - name: Clients
    description: Global client management (API consumers)
  - name: Client Attachments
    description: Client-route attachment management
  - name: Client Approvals
    description: Stage-based approval workflow for client attachments
  - name: Approvals
    description: Approval workflow
  - name: Kubernetes
    description: Kubernetes resource discovery
  - name: Metrics
    description: Route and domain observability metrics (Prometheus-backed)
  - name: System Settings
    description: Global system settings (owner only)
  - name: Topology
    description: Read-only project and domain topology views
  - name: Audit
    description: Audit logs
  - name: Client mTLS
    description: Client mTLS authentication management
  - name: AI
    description: AI-powered route generation and chat
  - name: Approval Policies
    description: Approval policy management (admin only)
  - name: Client Headers
    description: Client header authorization management
  - name: Client Methods
    description: Client HTTP method authorization management
  - name: SSO
    description: Single Sign-On configuration
  - name: DNS Credentials
    description: Platform-global DNS provider credential management (owner only)
  - name: Certificate Issuers
    description: >-
      Platform-global certificate issuer and issuer-project grant management
      (owner only)
  - name: Managed Certificates
    description: Project-scoped certificate issuance from a granted issuer
  - name: Notifications
    description: User notifications
  - name: Health
    description: Service health check
  - name: Docs
    description: API documentation
paths:
  /docs/openapi.yaml:
    get:
      tags:
        - Docs
      summary: Get OpenAPI specification
      description: Returns the bundled OpenAPI 3.0.3 specification for the FastGateway API
      operationId: getOpenAPISpec
      security:
        - bearerAuth: []
      responses:
        '200':
          description: OpenAPI specification in YAML format
          content:
            application/yaml; charset=utf-8:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
  /health:
    get:
      tags:
        - Health
      summary: Health check
      operationId: healthCheck
      responses:
        '200':
          description: Service is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
  /auth/login:
    post:
      tags:
        - Auth
      summary: Login with username and password
      operationId: login
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginRequest'
      responses:
        '200':
          description: Login successful
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /auth/logout:
    post:
      tags:
        - Auth
      summary: Logout and invalidate token
      operationId: logout
      security:
        - bearerAuth: []
      responses:
        '204':
          description: Logout successful
  /auth/refresh:
    post:
      tags:
        - Auth
      summary: Refresh access token
      operationId: refreshToken
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefreshTokenRequest'
      responses:
        '200':
          description: Token refreshed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /auth/me:
    get:
      tags:
        - Auth
      summary: Get current user info
      operationId: getCurrentUser
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Current user info
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CurrentUser'
  /auth/tokens:
    get:
      tags:
        - Auth
      summary: List API tokens for current user
      operationId: listApiTokens
      security:
        - bearerAuth: []
      responses:
        '200':
          description: List of API tokens
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ApiToken'
    post:
      tags:
        - Auth
      summary: Create new API token
      operationId: createApiToken
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateApiTokenRequest'
      responses:
        '201':
          description: Token created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiTokenCreated'
  /auth/tokens/{tokenId}:
    delete:
      tags:
        - Auth
      summary: Revoke API token
      operationId: revokeApiToken
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/tokenId'
      responses:
        '204':
          description: Token revoked
  /auth/password:
    put:
      tags:
        - Auth
      summary: Change password for current user
      operationId: changePassword
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChangePasswordRequest'
      responses:
        '200':
          description: Password changed successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Password changed successfully
        '400':
          description: Invalid request (wrong current password or weak new password)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /auth/tokens/capabilities:
    get:
      tags:
        - Auth
      summary: Get API token capabilities
      operationId: getApiTokenCapabilities
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Token capabilities
          content:
            application/json:
              schema:
                type: object
                properties:
                  enabled:
                    type: boolean
                    description: Whether API tokens feature is enabled
                  maxTokens:
                    type: integer
                    description: Maximum number of tokens allowed
                  currentCount:
                    type: integer
                    description: Current number of active tokens
  /my-teams:
    get:
      tags:
        - Teams
      summary: List teams the current user belongs to
      operationId: listMyTeams
      security:
        - bearerAuth: []
      responses:
        '200':
          description: List of user's teams
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Team'
  /auth/sso/config:
    get:
      tags:
        - SSO
      summary: Get public SSO configuration
      operationId: getSSOPublicConfig
      responses:
        '200':
          description: Public SSO configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SSOPublicConfig'
  /auth/sso/authorize:
    get:
      tags:
        - SSO
      summary: Initiate SSO authorization flow
      description: Redirects user to the OIDC identity provider for authentication
      operationId: ssoAuthorize
      responses:
        '302':
          description: Redirect to identity provider
        '400':
          description: SSO not configured
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /auth/sso/callback:
    get:
      tags:
        - SSO
      summary: Handle SSO callback from identity provider
      description: >-
        Processes the callback from the OIDC identity provider and redirects to
        frontend with tokens
      operationId: ssoCallback
      parameters:
        - name: code
          in: query
          schema:
            type: string
          description: Authorization code from IdP
        - name: state
          in: query
          schema:
            type: string
          description: CSRF state token
        - name: error
          in: query
          schema:
            type: string
          description: Error from IdP (if any)
        - name: error_description
          in: query
          schema:
            type: string
          description: Error description from IdP
      responses:
        '302':
          description: Redirect to frontend with tokens or error
  /settings/sso:
    get:
      tags:
        - SSO
      summary: Get SSO configuration (Owner only)
      operationId: getSSOConfig
      security:
        - bearerAuth: []
      responses:
        '200':
          description: SSO configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SSOConfig'
        '401':
          $ref: '#/components/responses/Unauthorized'
    put:
      tags:
        - SSO
      summary: Update SSO configuration (Owner only)
      operationId: updateSSOConfig
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SSOConfigInput'
      responses:
        '200':
          description: SSO configuration updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SSOConfig'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
    delete:
      tags:
        - SSO
      summary: Disable SSO (Owner only)
      operationId: disableSSO
      security:
        - bearerAuth: []
      responses:
        '200':
          description: SSO disabled
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: SSO disabled
        '401':
          $ref: '#/components/responses/Unauthorized'
  /settings/system:
    get:
      tags:
        - System Settings
      summary: Get system settings (Owner only)
      description: >-
        Returns the raw DB-stored values alongside the effective values (DB
        override if set, else env var/config default).
      operationId: getSystemSettings
      security:
        - bearerAuth: []
      responses:
        '200':
          description: System settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemSettingsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    put:
      tags:
        - System Settings
      summary: Update system settings (Owner only)
      description: |
        All fields are optional, but any field omitted from the request body is
        cleared to empty (falling back to its effective default) — this is a
        full replace, not a partial merge. jwtExpiry/refreshTokenExpiry must
        parse as Go duration strings (e.g. 24h, 1h30m); logLevel must be one of
        debug/info/warn/error.
      operationId: updateSystemSettings
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SystemSettingsInput'
      responses:
        '200':
          description: System settings updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemSettingsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /dns/credentials:
    get:
      tags:
        - DNS Credentials
      summary: List DNS provider credentials (Owner only)
      description: Platform-global. Never includes credential material.
      operationId: listDNSCredentials
      security:
        - bearerAuth: []
      responses:
        '200':
          description: List of DNS provider credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DNSProviderCredentialList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags:
        - DNS Credentials
      summary: Create DNS provider credential (Owner only)
      description: Credentials are encrypted at rest and never returned in any response.
      operationId: createDNSCredential
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDNSCredentialRequest'
      responses:
        '201':
          description: DNS provider credential created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DNSProviderCredential'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /dns/credentials/{dnsCredentialId}:
    get:
      tags:
        - DNS Credentials
      summary: Get DNS provider credential (Owner only)
      description: Metadata only, never includes credential material.
      operationId: getDNSCredential
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/dnsCredentialId'
      responses:
        '200':
          description: DNS provider credential
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DNSProviderCredential'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - DNS Credentials
      summary: Update DNS provider credential (Owner only)
      description: >-
        Update the name and/or rotate the credential values. Omitting
        `credentials` leaves the existing encrypted values untouched.
      operationId: updateDNSCredential
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/dnsCredentialId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateDNSCredentialRequest'
      responses:
        '200':
          description: DNS provider credential updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DNSProviderCredential'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - DNS Credentials
      summary: Delete DNS provider credential (Owner only)
      operationId: deleteDNSCredential
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/dnsCredentialId'
      responses:
        '204':
          description: DNS provider credential deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: DNS provider credential is referenced by an ACME issuer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /certificates:
    get:
      tags:
        - Managed Certificates
      summary: List managed certificates across all projects (Owner only)
      description: >-
        Owner-only fleet view of managed certificates across ALL projects,
        enriched with each certificate's resolved issuer name/type, Phase 3a
        distribution sync state, and referencing domains. Filterable by
        projectId to narrow to a single project.
      operationId: listFleetCertificates
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/limit'
        - name: status
          in: query
          required: false
          schema:
            type: string
            enum:
              - pending
              - issuing
              - ready
              - error
          description: Filter by certificate status.
        - name: usage
          in: query
          required: false
          schema:
            type: string
            enum:
              - server
              - client
          description: Filter by certificate usage.
        - name: issuerId
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Filter by issuer.
        - name: projectId
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Filter to a single project.
        - name: expiresBefore
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: Filter to certificates expiring before this RFC3339 timestamp.
      responses:
        '200':
          description: >-
            List of managed certificates across all projects, enriched with
            issuer, distribution, and domain info
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EnrichedManagedCertificateList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /certificates/issuers:
    get:
      tags:
        - Certificate Issuers
      summary: List certificate issuers (Owner only)
      description: Platform-global. Never includes key material.
      operationId: listCertificateIssuers
      security:
        - bearerAuth: []
      responses:
        '200':
          description: List of certificate issuers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CertificateIssuerList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags:
        - Certificate Issuers
      summary: Create certificate issuer (Owner only)
      description: >-
        Body discriminated by `type` (self_signed_ca or acme). Creates the
        corresponding cert-manager CRDs in the control cluster.
      operationId: createCertificateIssuer
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCertificateIssuerRequest'
      responses:
        '201':
          description: Certificate issuer created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CertificateIssuer'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /certificates/issuers/{issuerId}:
    get:
      tags:
        - Certificate Issuers
      summary: Get certificate issuer (Owner only)
      operationId: getCertificateIssuer
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/issuerId'
      responses:
        '200':
          description: Certificate issuer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CertificateIssuer'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - Certificate Issuers
      summary: Delete certificate issuer (Owner only)
      description: >-
        Removes the underlying cert-manager CRDs. 409 if referenced by any
        ManagedCertificate.
      operationId: deleteCertificateIssuer
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/issuerId'
      responses:
        '204':
          description: Certificate issuer deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: Certificate issuer is referenced by a ManagedCertificate
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /certificates/issuers/{issuerId}/status:
    get:
      tags:
        - Certificate Issuers
      summary: Get certificate issuer readiness (Owner only)
      description: >-
        cert-manager readiness of the underlying Issuer (Ready condition +
        message).
      operationId: getCertificateIssuerStatus
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/issuerId'
      responses:
        '200':
          description: Certificate issuer status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CertificateIssuerStatus'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /certificates/issuers/{issuerId}/grants:
    get:
      tags:
        - Certificate Issuers
      summary: List projects an issuer is granted to (Owner only)
      operationId: listIssuerGrants
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/issuerId'
      responses:
        '200':
          description: List of issuer-project grants
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IssuerProjectGrantList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags:
        - Certificate Issuers
      summary: Grant an issuer to a project (Owner only)
      operationId: createIssuerGrant
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/issuerId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateIssuerProjectGrantRequest'
      responses:
        '201':
          description: Issuer granted to the project
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /certificates/issuers/{issuerId}/grants/{projectId}:
    delete:
      tags:
        - Certificate Issuers
      summary: Revoke an issuer's grant to a project (Owner only)
      description: 409 if a certificate in that project still uses the issuer.
      operationId: deleteIssuerGrant
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/issuerId'
        - $ref: '#/components/parameters/projectId'
      responses:
        '204':
          description: Grant revoked
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: A certificate in this project still uses the issuer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /notifications:
    get:
      tags:
        - Notifications
      summary: List notifications for current user
      operationId: listNotifications
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/limit'
        - name: unread
          in: query
          schema:
            type: string
            enum:
              - 'true'
          description: Filter to unread notifications only
      responses:
        '200':
          description: Paginated notification list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotificationList'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /notifications/count:
    get:
      tags:
        - Notifications
      summary: Get unread notification count
      operationId: getUnreadNotificationCount
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Unread count
          content:
            application/json:
              schema:
                type: object
                properties:
                  unread:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
  /notifications/{notificationId}/read:
    put:
      tags:
        - Notifications
      summary: Mark notification as read
      operationId: markNotificationAsRead
      security:
        - bearerAuth: []
      parameters:
        - name: notificationId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Marked as read
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '404':
          $ref: '#/components/responses/NotFound'
  /notifications/read-all:
    put:
      tags:
        - Notifications
      summary: Mark all notifications as read
      operationId: markAllNotificationsAsRead
      security:
        - bearerAuth: []
      responses:
        '200':
          description: All marked as read
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
  /users:
    get:
      tags:
        - Users
      summary: List all users (Owner only)
      operationId: listUsers
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/limit'
        - name: role
          in: query
          schema:
            type: string
            enum:
              - owner
              - user
      responses:
        '200':
          description: List of users
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserList'
    post:
      tags:
        - Users
      summary: Create new user (Owner only)
      operationId: createUser
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
      responses:
        '201':
          description: User created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
  /users/{userId}:
    get:
      tags:
        - Users
      summary: Get user by ID
      operationId: getUser
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/userId'
      responses:
        '200':
          description: User details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Users
      summary: Update user
      operationId: updateUser
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/userId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateUserRequest'
      responses:
        '200':
          description: User updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
    delete:
      tags:
        - Users
      summary: Delete user
      operationId: deleteUser
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/userId'
      responses:
        '204':
          description: User deleted
  /teams:
    get:
      tags:
        - Teams
      summary: List all global teams
      operationId: listTeams
      security:
        - bearerAuth: []
      responses:
        '200':
          description: List of teams
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Team'
    post:
      tags:
        - Teams
      summary: Create global team (Owner only)
      operationId: createTeam
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTeamRequest'
      responses:
        '201':
          description: Team created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Team'
  /teams/{teamId}:
    get:
      tags:
        - Teams
      summary: Get team by ID
      operationId: getTeam
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200':
          description: Team details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Team'
    patch:
      tags:
        - Teams
      summary: Update team
      operationId: updateTeam
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTeamRequest'
      responses:
        '200':
          description: Team updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Team'
    delete:
      tags:
        - Teams
      summary: Delete team
      operationId: deleteTeam
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '204':
          description: Team deleted
  /teams/{teamId}/members:
    get:
      tags:
        - Teams
      summary: List team members
      operationId: listTeamMembers
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200':
          description: List of team members
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'
    post:
      tags:
        - Teams
      summary: Add member to team
      operationId: addTeamMember
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddTeamMemberRequest'
      responses:
        '201':
          description: Member added
  /teams/{teamId}/members/{userId}:
    delete:
      tags:
        - Teams
      summary: Remove member from team
      operationId: removeTeamMember
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/teamId'
        - $ref: '#/components/parameters/userId'
      responses:
        '204':
          description: Member removed
  /teams/{teamId}/projects:
    get:
      tags:
        - Teams
      summary: List projects a team is assigned to
      operationId: listTeamProjects
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200':
          description: List of projects the team is assigned to
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectTeamRole'
  /teams/{teamId}/members/email:
    post:
      tags:
        - Teams
      summary: Add member to team by email (Owner only)
      description: If user exists, adds directly. If not, creates a pending invite.
      operationId: addTeamMemberByEmail
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddMemberByEmailRequest'
      responses:
        '200':
          description: Member added or invited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AddMemberResult'
        '400':
          $ref: '#/components/responses/BadRequest'
  /teams/{teamId}/invites:
    get:
      tags:
        - Teams
      summary: List pending invites for team (Owner only)
      operationId: listTeamInvites
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/teamId'
      responses:
        '200':
          description: List of pending invites
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TeamEmailInvite'
  /teams/{teamId}/invites/{inviteId}:
    delete:
      tags:
        - Teams
      summary: Delete a pending invite (Owner only)
      operationId: deleteTeamInvite
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/teamId'
        - name: inviteId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Invite deleted
  /projects:
    get:
      tags:
        - Projects
      summary: List projects
      operationId: listProjects
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: List of projects
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectList'
    post:
      tags:
        - Projects
      summary: Create new project (Owner only)
      operationId: createProject
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectRequest'
      responses:
        '201':
          description: Project created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
  /projects/{projectId}:
    get:
      tags:
        - Projects
      summary: Get project by ID
      operationId: getProject
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: Project details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
    patch:
      tags:
        - Projects
      summary: Update project
      operationId: updateProject
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProjectRequest'
      responses:
        '200':
          description: Project updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
    delete:
      tags:
        - Projects
      summary: Delete project
      operationId: deleteProject
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '204':
          description: Project deleted
  /projects/{projectId}/test-connection:
    post:
      tags:
        - Projects
      summary: Test Kubernetes connection
      operationId: testProjectConnection
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: Connection test result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectionTestResult'
  /projects/{projectId}/metrics/test-connection:
    post:
      tags:
        - Metrics
      summary: Test connectivity to the project's configured Prometheus endpoint
      operationId: testMetricsConnection
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: >-
            Connection test result (ok is false on failure; see error for
            details)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TestConnectionResult'
        '400':
          $ref: '#/components/responses/BadRequest'
  /projects/{projectId}/capabilities:
    get:
      tags:
        - Projects
      summary: Get project capabilities
      description: >-
        Returns available features for the project based on Kubernetes cluster
        configuration
      operationId: getProjectCapabilities
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: Project capabilities
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectCapabilities'
        '400':
          $ref: '#/components/responses/BadRequest'
  /projects/{projectId}/versions:
    get:
      tags:
        - Projects
      summary: Get detected Envoy Gateway / Gateway API versions for project
      description: >-
        Returns the cached (or freshly-detected, if the cache is cold or
        expired) version-compatibility info for the project's cluster.
      operationId: getProjectVersions
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: Version info
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VersionInfo'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          description: Version detection failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/versions/refresh:
    post:
      tags:
        - Projects
      summary: Invalidate cached version info and re-detect
      description: >-
        Drops the cached version info for the project, then immediately
        re-detects and re-caches it.
      operationId: refreshProjectVersions
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: Freshly-detected version info
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VersionInfo'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          description: Version detection failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/permissions:
    get:
      tags:
        - Projects
      summary: Get current user permissions for project
      operationId: getProjectPermissions
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: User permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectPermissions'
  /projects/{projectId}/admins:
    get:
      tags:
        - Projects
      summary: List project admins
      operationId: listProjectAdmins
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: List of project admins
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'
    post:
      tags:
        - Projects
      summary: Add admin to project
      operationId: addProjectAdmin
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddProjectAdminRequest'
      responses:
        '201':
          description: Admin added
  /projects/{projectId}/admins/{userId}:
    delete:
      tags:
        - Projects
      summary: Remove admin from project
      operationId: removeProjectAdmin
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/userId'
      responses:
        '204':
          description: Admin removed
  /projects/{projectId}/members:
    get:
      tags:
        - Projects
      summary: List project members
      description: >-
        Returns all users who have access to the project (admins, team members,
        etc.)
      operationId: listProjectMembers
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - name: search
          in: query
          schema:
            type: string
          description: Free-text search term to filter members
      responses:
        '200':
          description: List of project members
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'
  /projects/{projectId}/my-teams:
    get:
      tags:
        - Projects
      summary: List current user's teams in project
      operationId: listMyTeamsInProject
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: List of user's teams in the project
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectTeamRole'
  /projects/{projectId}/teams:
    get:
      tags:
        - Project Teams
      summary: List teams assigned to project
      operationId: listProjectTeams
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: List of project team roles
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectTeamRole'
    post:
      tags:
        - Project Teams
      summary: Assign team to project with role
      operationId: assignTeamToProject
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssignTeamRequest'
      responses:
        '201':
          description: Team assigned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectTeamRole'
  /projects/{projectId}/teams/{teamId}:
    get:
      tags:
        - Project Teams
      summary: Get project team role by team ID
      operationId: getProjectTeam
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/teamId'
      responses:
        '200':
          description: Project team role details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectTeamRole'
    patch:
      tags:
        - Project Teams
      summary: Update team presets in project
      operationId: updateProjectTeamPresets
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/teamId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTeamPresetsInput'
      responses:
        '200':
          description: Team presets updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectTeamRole'
    delete:
      tags:
        - Project Teams
      summary: Remove team from project
      operationId: removeTeamFromProject
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/teamId'
      responses:
        '204':
          description: Team removed from project
  /projects/{projectId}/presets:
    get:
      tags:
        - Permission Presets
      summary: List permission presets for project
      operationId: listPresets
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: List of permission presets
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/PermissionPreset'
        '400':
          $ref: '#/components/responses/BadRequest'
    post:
      tags:
        - Permission Presets
      summary: Create permission preset
      operationId: createPreset
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePresetRequest'
      responses:
        '201':
          description: Preset created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PermissionPreset'
        '400':
          $ref: '#/components/responses/BadRequest'
  /projects/{projectId}/presets/{presetId}:
    get:
      tags:
        - Permission Presets
      summary: Get permission preset by ID
      operationId: getPreset
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - name: presetId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Preset details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PermissionPreset'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Permission Presets
      summary: Update permission preset
      operationId: updatePreset
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - name: presetId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePresetRequest'
      responses:
        '200':
          description: Preset updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PermissionPreset'
        '400':
          $ref: '#/components/responses/BadRequest'
    delete:
      tags:
        - Permission Presets
      summary: Delete permission preset
      operationId: deletePreset
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - name: presetId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Preset deleted
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/domain-templates:
    get:
      tags:
        - Domain Templates
      summary: List domain templates in project
      operationId: listDomainTemplates
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: List of domain templates
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainTemplateList'
    post:
      tags:
        - Domain Templates
      summary: Create domain template
      operationId: createDomainTemplate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDomainTemplateRequest'
      responses:
        '201':
          description: Domain template created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainTemplate'
  /projects/{projectId}/domain-templates/{domainTemplateId}:
    get:
      tags:
        - Domain Templates
      summary: Get domain template by ID
      operationId: getDomainTemplate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainTemplateId'
      responses:
        '200':
          description: Domain template details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainTemplate'
    patch:
      tags:
        - Domain Templates
      summary: Update domain template
      operationId: updateDomainTemplate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainTemplateId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateDomainTemplateRequest'
      responses:
        '200':
          description: Domain template updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainTemplate'
    delete:
      tags:
        - Domain Templates
      summary: Delete domain template
      operationId: deleteDomainTemplate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainTemplateId'
      responses:
        '204':
          description: Domain template deleted
  /projects/{projectId}/domain-templates/{domainTemplateId}/manifests:
    get:
      tags:
        - Domain Templates
      summary: Get domain template Kubernetes manifests
      operationId: getDomainTemplateManifests
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainTemplateId'
      responses:
        '200':
          description: Template manifests
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainTemplateManifests'
  /projects/{projectId}/domain-templates/{domainTemplateId}/domains:
    get:
      tags:
        - Domain Templates
      summary: List domains using this template
      operationId: listDomainsByTemplate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainTemplateId'
      responses:
        '200':
          description: List of domains using this template
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Domain'
  /projects/{projectId}/domain-templates/preview-create:
    post:
      tags:
        - Domain Templates
      summary: Preview domain template creation
      operationId: previewDomainTemplateCreate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DomainTemplateCreatePreviewRequest'
      responses:
        '200':
          description: Template creation preview
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainTemplateCreatePreviewResult'
  /projects/{projectId}/domain-templates/{domainTemplateId}/preview-changes:
    post:
      tags:
        - Domain Templates
      summary: Preview domain template changes
      operationId: previewDomainTemplateChanges
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainTemplateId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DomainTemplatePreviewChangesRequest'
      responses:
        '200':
          description: Template changes preview
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainTemplatePreviewResult'
  /projects/{projectId}/namespaces:
    get:
      tags:
        - Project Namespaces
      summary: List whitelisted namespaces for project
      operationId: listProjectNamespaces
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: List of project namespaces
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProjectNamespace'
    post:
      tags:
        - Project Namespaces
      summary: Add namespace to project whitelist
      operationId: addProjectNamespace
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProjectNamespaceRequest'
      responses:
        '201':
          description: Namespace added
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectNamespace'
  /projects/{projectId}/namespaces/{namespaceId}:
    get:
      tags:
        - Project Namespaces
      summary: Get project namespace by ID
      operationId: getProjectNamespace
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/namespaceId'
      responses:
        '200':
          description: Project namespace details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectNamespace'
    patch:
      tags:
        - Project Namespaces
      summary: Update capabilities for a project namespace
      operationId: updateProjectNamespace
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/namespaceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProjectNamespaceRequest'
      responses:
        '200':
          description: Updated namespace
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectNamespace'
    delete:
      tags:
        - Project Namespaces
      summary: Remove namespace from project whitelist
      operationId: removeProjectNamespace
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/namespaceId'
      responses:
        '204':
          description: Namespace removed
  /projects/{projectId}/namespaces/{namespaceId}/ensure-reference-grant:
    post:
      tags:
        - Project Namespaces
      summary: Ensure ReferenceGrant exists for namespace
      operationId: ensureReferenceGrant
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/namespaceId'
      responses:
        '200':
          description: ReferenceGrant ensured; returns the project namespace
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectNamespace'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/domains:
    get:
      tags:
        - Domains
      summary: List domains in project
      operationId: listDomains
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: List of domains
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainList'
    post:
      tags:
        - Domains
      summary: Create domain (Admin only)
      operationId: createDomain
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDomainRequest'
      responses:
        '201':
          description: Domain created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Domain'
  /projects/{projectId}/domains/tls-secrets:
    get:
      tags:
        - Domains
      summary: List TLS secrets available for domain TLS configuration
      description: >-
        Lists kubernetes.io/tls Secrets in a namespace, annotated with whether
        FastGateway manages them, plus the namespaces available for lookup in
        this project.
      operationId: listDomainTLSSecrets
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - name: namespace
          in: query
          required: false
          schema:
            type: string
          description: >-
            Kubernetes namespace to list TLS secrets from. Defaults to the
            project's default namespace.
      responses:
        '200':
          description: TLS secrets available in the namespace
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListTLSSecretsResponse'
        '400':
          description: >-
            Namespace not managed by this project, or namespace management not
            configured
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/domains/available-namespaces:
    get:
      tags:
        - Domains
      summary: List namespaces eligible for domain deployment
      operationId: listDomainAvailableNamespaces
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: Namespaces eligible for domain deployment in this project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvailableNamespacesResponse'
  /projects/{projectId}/domains/{domainId}:
    get:
      tags:
        - Domains
      summary: Get domain by ID
      operationId: getDomain
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      responses:
        '200':
          description: Domain details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Domain'
    patch:
      tags:
        - Domains
      summary: Update domain (Admin only)
      operationId: updateDomain
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateDomainRequest'
      responses:
        '200':
          description: Domain updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Domain'
    delete:
      tags:
        - Domains
      summary: Delete domain (Admin only)
      operationId: deleteDomain
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      responses:
        '204':
          description: Domain deleted
  /projects/{projectId}/domains/{domainId}/settings:
    get:
      tags:
        - Domains
      summary: Get domain settings
      description: >-
        Returns the domain-level settings configuration. This is a
        gateway-agnostic API.
      operationId: getDomainSettings
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      responses:
        '200':
          description: Domain settings ({} with all fields omitted if not configured)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainSettings'
    put:
      tags:
        - Domains
      summary: Update domain settings (Admin only)
      description: >
        Updates the domain-level settings. This is a gateway-agnostic API that
        gets translated

        to the appropriate gateway-specific resources (e.g., Envoy Gateway
        ClientTrafficPolicy).

        Requires Owner or Project Admin role.
      operationId: updateDomainSettings
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateDomainSettingsRequest'
      responses:
        '200':
          description: >-
            Domain settings updated, with any operator-facing warnings (null if
            all settings disabled)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdateDomainSettingsResponse'
        '403':
          description: Access denied - only project admins can manage domain settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/domains/{domainId}/certificate:
    put:
      tags:
        - Domains
      summary: Attach a managed certificate to a domain (Admin only)
      description: >
        Points the domain's Gateway listener at a `usage=server` managed
        certificate

        and re-applies it. Requires Owner, Project Admin, or domain-manage
        permission

        (the same access level as domain settings).
      operationId: attachDomainCertificate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - certificateId
              properties:
                certificateId:
                  type: string
                  format: uuid
      responses:
        '200':
          description: Certificate attached; returns the updated domain
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Domain'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Domain or certificate not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Certificate has the wrong usage, or is not yet ready
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - Domains
      summary: Detach the managed certificate from a domain (Admin only)
      description: >
        Clears the domain's managed certificate and re-applies its Gateway
        listener,

        which reverts to any legacy BYO TLS secret configured on the domain.
        Requires

        Owner, Project Admin, or domain-manage permission.
      operationId: detachDomainCertificate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      responses:
        '200':
          description: Certificate detached; returns the updated domain
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Domain'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/domains/{domainId}/settings/mtls/ca:
    post:
      tags:
        - Domains
      summary: Add CA certificate for domain mTLS
      operationId: addDomainMTLSCA
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddDomainMTLSCARequest'
      responses:
        '200':
          description: CA certificate added; returns the updated domain settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainSettingsRecord'
        '403':
          $ref: '#/components/responses/Forbidden'
  /projects/{projectId}/domains/{domainId}/settings/mtls/ca/{caId}:
    delete:
      tags:
        - Domains
      summary: Remove CA certificate from domain mTLS
      operationId: removeDomainMTLSCA
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - name: caId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: CA certificate removed; returns the updated domain settings
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainSettingsRecord'
        '403':
          $ref: '#/components/responses/Forbidden'
  /projects/{projectId}/domains/{domainId}/yamls:
    get:
      tags:
        - Domains
      summary: Get domain Kubernetes YAMLs
      operationId: getDomainYamls
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      responses:
        '200':
          description: Domain YAML manifests
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainYAMLs'
  /projects/{projectId}/domains/{domainId}/settings/preview:
    post:
      tags:
        - Domains
      summary: Preview domain settings changes
      operationId: previewDomainSettingsChanges
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DomainSettingsPreviewInput'
      responses:
        '200':
          description: Settings change preview
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainSettingsPreviewResult'
  /projects/{projectId}/domains/preview-create:
    post:
      tags:
        - Domains
      summary: Preview domain creation
      operationId: previewDomainCreate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DomainCreatePreviewInput'
      responses:
        '200':
          description: Domain creation preview
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainCreatePreviewResult'
  /projects/{projectId}/domains/{domainId}/metrics:
    get:
      tags:
        - Metrics
      summary: >-
        Get request-rate, error-rate, and latency panels for a domain, plus
        top-5 route tables
      operationId: getDomainMetrics
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - name: range
          in: query
          description: Lookback window for the query
          schema:
            type: string
            enum:
              - 15m
              - 1h
              - 6h
              - 24h
              - 7d
            default: 1h
      responses:
        '200':
          description: Domain metrics panels and top-routes tables
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainMetricsResult'
        '400':
          description: Invalid range, or metrics endpoint not configured for the project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '502':
          description: Upstream Prometheus query failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '504':
          description: Upstream Prometheus query timed out
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/domains/{domainId}/topology:
    get:
      tags:
        - Topology
      summary: Get the per-domain topology view
      description: |
        Returns the domain's gateway status, routes, backends, and (in client
        security mode) attached clients and per-attachment enforcement flags.
      operationId: getDomainTopology
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      responses:
        '200':
          description: Domain topology
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DomainTopologyResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          description: |
            Topology resource not found. Returned with a deliberately generic
            body (no entity name/ID) to avoid leaking cross-project existence.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/domains/{domainId}/import/openapi:
    post:
      tags:
        - Routes
      summary: Import routes from an OpenAPI spec
      description: >
        Parses an uploaded OpenAPI 3.0/3.1 spec and returns a list of candidate

        routes (not yet created) for the caller to review and submit
        individually

        via createRoute. The request body is capped at 5MB.
      operationId: importOpenAPI
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OpenAPIImportRequest'
      responses:
        '200':
          description: Parsed routes, warnings, renames, and spec metadata
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OpenAPIImportResponse'
        '400':
          description: >-
            Invalid request body, invalid defaultBackend, or the spec failed to
            parse
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImportError'
        '413':
          description: Spec body exceeded the 5MB limit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImportError'
  /projects/{projectId}/domains/{domainId}/routes:
    get:
      tags:
        - Routes
      summary: List routes for domain
      operationId: listRoutes
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/limit'
        - name: teamId
          in: query
          schema:
            type: string
            format: uuid
          description: Filter by team
        - name: status
          in: query
          schema:
            type: string
            enum:
              - pending_create
              - pending_update
              - pending_delete
              - approved
              - pending_deploy
              - active
              - rejected
          description: >-
            Filter by a single status value (unlike listProjectRoutes, this does
            not accept a comma-separated list)
        - name: search
          in: query
          schema:
            type: string
          description: Free-text search term
        - name: searchField
          in: query
          schema:
            type: string
            enum:
              - all
              - name
              - path
              - owner
          description: >-
            Field to restrict the search term to (defaults to searching all
            fields)
        - name: labels
          in: query
          schema:
            type: string
          description: >-
            Label filter expression (e.g. "key=value,key2=value2") matched
            against the route's labels
      responses:
        '200':
          description: List of routes
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouteList'
    post:
      tags:
        - Routes
      summary: Create route (submits for approval)
      operationId: createRoute
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateRouteRequest'
      responses:
        '201':
          description: Route created and pending approval
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouteResponse'
  /projects/{projectId}/domains/{domainId}/routes/{routeId}:
    get:
      tags:
        - Routes
      summary: Get route by ID
      operationId: getRoute
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
      responses:
        '200':
          description: Route details, including its associated policies (if configured)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouteWithPolicies'
    put:
      tags:
        - Routes
      summary: Update route (submits for approval)
      operationId: updateRoute
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateRouteRequest'
      responses:
        '200':
          description: Route update pending approval
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouteResponse'
    delete:
      tags:
        - Routes
      summary: Delete route (submits for approval)
      operationId: deleteRoute
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
      responses:
        '200':
          description: Route deletion pending approval
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Route'
  /projects/{projectId}/domains/{domainId}/routes/{routeId}/yaml:
    get:
      tags:
        - Routes
      summary: Get route as Kubernetes YAML (HTTPRoute only)
      operationId: getRouteYaml
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
      responses:
        '200':
          description: Route as YAML
          content:
            text/yaml:
              schema:
                type: string
  /projects/{projectId}/domains/{domainId}/routes/{routeId}/yamls:
    get:
      tags:
        - Routes
      summary: >-
        Get all Kubernetes YAMLs for route (HTTPRoute, SecurityPolicy,
        BackendTrafficPolicy)
      operationId: getRouteYamls
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
      responses:
        '200':
          description: Route YAMLs
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouteYAMLsResponse'
  /projects/{projectId}/domains/{domainId}/routes/preview:
    post:
      tags:
        - Routes
      summary: Preview route creation (generate YAML without creating)
      operationId: previewCreateRoute
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateRouteRequest'
      responses:
        '200':
          description: Preview of route YAML
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreviewCreateResponse'
  /projects/{projectId}/domains/{domainId}/routes/check-conflicts:
    post:
      tags:
        - Routes
      summary: >-
        Check whether a route matcher conflicts with existing routes in the
        domain
      operationId: checkRouteConflicts
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CheckConflictsRequest'
      responses:
        '200':
          description: Routes (if any) whose matcher conflicts with the given matcher
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckConflictsResponse'
        '400':
          description: Invalid request body or domain ID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/domains/{domainId}/routes/{routeId}/preview:
    post:
      tags:
        - Routes
      summary: Preview route update (compare current vs proposed YAML)
      operationId: previewUpdateRoute
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateRouteRequest'
      responses:
        '200':
          description: Preview of route update
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreviewUpdateResponse'
  /projects/{projectId}/domains/{domainId}/routes/{routeId}/preview-delete:
    get:
      tags:
        - Routes
      summary: Preview route deletion (show what will be deleted)
      operationId: previewDeleteRoute
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
      responses:
        '200':
          description: Preview of route deletion
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreviewDeleteResponse'
  /projects/{projectId}/domains/{domainId}/routes/{routeId}/deploy:
    post:
      tags:
        - Routes
      summary: Deploy approved route to Kubernetes
      operationId: deployRoute
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
      responses:
        '200':
          description: Route deployed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Route'
        '400':
          description: Route not in deployable state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/domains/{domainId}/routes/{routeId}/versions:
    get:
      tags:
        - Routes
      summary: List route version history
      operationId: listRouteVersions
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: Route version history
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouteVersionList'
  /projects/{projectId}/domains/{domainId}/routes/{routeId}/versions/{version}:
    get:
      tags:
        - Routes
      summary: Get specific route version
      operationId: getRouteVersion
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
        - name: version
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Route version details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouteVersion'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/domains/{domainId}/routes/{routeId}/versions/{version}/rollback:
    post:
      tags:
        - Routes
      summary: Rollback route to a previous version
      operationId: rollbackRouteVersion
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
        - name: version
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Route rolled back
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Route'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/domains/{domainId}/routes/{routeId}/metrics:
    get:
      tags:
        - Metrics
      summary: Get request-rate, error-rate, and latency panels for a single route
      operationId: getRouteMetrics
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
        - name: range
          in: query
          description: Lookback window for the query
          schema:
            type: string
            enum:
              - 15m
              - 1h
              - 6h
              - 24h
              - 7d
            default: 1h
      responses:
        '200':
          description: Route metrics panels
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouteMetricsResult'
        '400':
          description: Invalid range, or metrics endpoint not configured for the project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '502':
          description: Upstream Prometheus query failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '504':
          description: Upstream Prometheus query timed out
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/routes:
    get:
      tags:
        - Routes
      summary: List routes across all domains in a project
      description: |
        Lists routes in a project, optionally filtered by backend service and
        namespace. When `backend_service` and `backend_namespace` are both set,
        only routes whose `config.backends[]` (and optionally `config.mirrors[]`
        when `include_mirrors=true`) contain a Kubernetes backend with that
        service+namespace are returned. Useful for answering "where is this
        service used?".
      operationId: listProjectRoutes
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - name: backend_service
          in: query
          schema:
            type: string
          description: >-
            Match routes whose backends contain a Kubernetes backend with this
            service name. Must be paired with backend_namespace.
        - name: backend_namespace
          in: query
          schema:
            type: string
          description: >-
            Match routes whose backends contain a Kubernetes backend in this
            namespace. Must be paired with backend_service.
        - name: include_mirrors
          in: query
          schema:
            type: boolean
            default: false
          description: When true, also match against config.mirrors[].
        - name: status
          in: query
          schema:
            type: string
          description: >-
            Comma-separated list of statuses (pending_create, pending_update,
            pending_delete, approved, pending_deploy, active, rejected).
        - name: team_id
          in: query
          schema:
            type: string
            format: uuid
        - name: domain_id
          in: query
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/page'
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            maximum: 200
      responses:
        '200':
          description: Paginated list of routes
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouteList'
        '400':
          description: Invalid filter (e.g., backend_service without backend_namespace)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /clients:
    get:
      tags:
        - Clients
      summary: List clients
      description: Owner sees all clients. Others see clients belonging to their teams.
      operationId: listClients
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/limit'
        - name: teamId
          in: query
          schema:
            type: string
            format: uuid
          description: Filter by team
      responses:
        '200':
          description: List of clients
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientList'
    post:
      tags:
        - Clients
      summary: Create client (Owner or team member)
      operationId: createClient
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateClientRequest'
      responses:
        '201':
          description: Client created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Client'
        '403':
          $ref: '#/components/responses/Forbidden'
  /clients/{clientId}:
    get:
      tags:
        - Clients
      summary: Get client by ID
      operationId: getClient
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      responses:
        '200':
          description: Client details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Client'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Clients
      summary: Update client (Owner or team member)
      operationId: updateClient
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateClientRequest'
      responses:
        '200':
          description: Client updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Client'
        '403':
          $ref: '#/components/responses/Forbidden'
    delete:
      tags:
        - Clients
      summary: Delete client (Owner or team member)
      operationId: deleteClient
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      responses:
        '204':
          description: Client deleted
        '403':
          $ref: '#/components/responses/Forbidden'
  /clients/{clientId}/ips:
    get:
      tags:
        - Client IPs
      summary: List IP addresses for a client (Owner or team member)
      operationId: listClientIPs
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      responses:
        '200':
          description: List of client IP addresses
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ClientIPAddress'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags:
        - Client IPs
      summary: Add IP address to client (Owner or team member)
      operationId: addClientIP
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateClientIPRequest'
      responses:
        '201':
          description: IP address added
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientIPAddress'
        '403':
          $ref: '#/components/responses/Forbidden'
  /clients/{clientId}/ips/{ipId}:
    delete:
      tags:
        - Client IPs
      summary: Remove IP address from client (Owner or team member)
      operationId: removeClientIP
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
        - $ref: '#/components/parameters/ipId'
      responses:
        '204':
          description: IP address removed
        '403':
          $ref: '#/components/responses/Forbidden'
  /clients/{clientId}/api-key:
    post:
      tags:
        - Client API Key
      summary: Generate API key for client (Owner or team member)
      description: >
        Generates a new API key for the client. If the client already has an API
        key,

        it will be replaced with a new one. The plaintext key is returned only
        once

        in the response and cannot be retrieved again.
      operationId: generateClientAPIKey
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GenerateAPIKeyRequest'
      responses:
        '200':
          description: API key generated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenerateAPIKeyResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - Client API Key
      summary: Revoke API key for client (Owner or team member)
      description: >
        Revokes the client's API key. Routes with this client attached using API
        key

        authentication will move to pending_deploy status.
      operationId: revokeClientAPIKey
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      responses:
        '204':
          description: API key revoked successfully
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /clients/{clientId}/jwt:
    post:
      tags:
        - Client JWT
      summary: Configure JWT authentication for client (Owner or team member)
      description: >
        Configures JWT authentication for the client. Requires a valid issuer
        URL

        and JWKS URL. Optionally accepts audience values, required claims, and

        claim-to-header mappings.
      operationId: configureClientJWT
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfigureJWTRequest'
      responses:
        '200':
          description: JWT configured successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfigureJWTResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
        - Client JWT
      summary: Update JWT authentication for client (Owner or team member)
      description: >
        Updates the JWT authentication configuration for the client. The client
        must

        already have JWT configured. Routes with this client attached using JWT

        authentication will move to pending_deploy status.
      operationId: updateClientJWT
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfigureJWTRequest'
      responses:
        '200':
          description: JWT updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfigureJWTResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - Client JWT
      summary: Remove JWT authentication from client (Owner or team member)
      description: >
        Removes JWT authentication from the client. Routes with this client
        attached

        using JWT authentication will move to pending_deploy status.
      operationId: removeClientJWT
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      responses:
        '204':
          description: JWT removed successfully
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /clients/{clientId}/headers:
    get:
      tags:
        - Client Headers
      summary: List headers for a client (Owner or team member)
      operationId: listClientHeaders
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      responses:
        '200':
          description: List of client headers
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ClientHeader'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags:
        - Client Headers
      summary: Add header to client (Owner or team member)
      description: >
        Adds a header authorization rule to the client. The header name and
        values

        are used to match incoming requests for authorization. Routes with this

        client attached will move to pending_deploy status.
      operationId: addClientHeader
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateClientHeaderRequest'
      responses:
        '201':
          description: Header added
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientHeader'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
  /clients/{clientId}/headers/{headerId}:
    delete:
      tags:
        - Client Headers
      summary: Remove header from client (Owner or team member)
      description: |
        Removes a header authorization rule from the client. Routes with this
        client attached will move to pending_deploy status.
      operationId: removeClientHeader
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
        - $ref: '#/components/parameters/headerId'
      responses:
        '204':
          description: Header removed
        '403':
          $ref: '#/components/responses/Forbidden'
  /clients/{clientId}/methods:
    put:
      tags:
        - Client Methods
      summary: Set allowed HTTP methods for client (Owner or team member)
      description: |
        Sets the allowed HTTP methods for the client. These methods are used
        in authorization rules when the client is attached to routes. Valid
        methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS. Pass an empty
        array to clear all method restrictions. Routes with this client
        attached will move to pending_deploy status.
      operationId: setClientAllowedMethods
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetAllowedMethodsRequest'
      responses:
        '200':
          description: Methods updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Client'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
  /clients/{clientId}/mtls:
    put:
      tags:
        - Client mTLS
      summary: Configure mTLS authentication for client (Owner or team member)
      operationId: updateClientMTLS
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateClientMTLSRequest'
      responses:
        '200':
          description: mTLS configured successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Client'
        '403':
          $ref: '#/components/responses/Forbidden'
    delete:
      tags:
        - Client mTLS
      summary: Remove mTLS authentication from client (Owner or team member)
      operationId: removeClientMTLS
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      responses:
        '204':
          description: mTLS removed successfully
        '403':
          $ref: '#/components/responses/Forbidden'
  /clients/{clientId}/certificate:
    put:
      tags:
        - Client mTLS
      summary: Attach a managed client certificate to a client (team member)
      description: |
        Attaches a managed, usage=client certificate (issued via
        POST /projects/{projectId}/certificates) to this client's mTLS
        configuration. Writes only the client row -- the CA Secret and
        ClientTrafficPolicy it implies materialize at the next domain deploy,
        exactly like PUT .../mtls. Requires the caller to be a member of the
        client's team, and the certificate to be usage=client, status=ready,
        in a project the client's team has access to, and not already
        attached elsewhere.
      operationId: attachClientCertificate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AttachClientCertificateRequest'
      responses:
        '200':
          description: Certificate attached; returns the updated client
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Client'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Client or certificate not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: >-
            The certificate is already attached to another client, or this
            client already has a managed certificate attached.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: The certificate is not usage=client, or is not yet ready.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      tags:
        - Client mTLS
      summary: Detach the managed certificate from a client (team member)
      description: |
        Clears the client's managed certificate and its derived mTLS fields,
        reverting the client to no mTLS. A previously-configured BYO mTLS
        configuration is not restored automatically -- re-add it via
        PUT .../mtls if wanted.
      operationId: detachClientCertificate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      responses:
        '200':
          description: Certificate detached; returns the updated client
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Client'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /clients/{clientId}/attachable-certificates:
    get:
      tags:
        - Client mTLS
      summary: List managed certificates this client could attach (team member)
      description: |
        Lists managed client-usage certificates that are ready, in a project
        the client's team has a role in, and not already attached to any
        client -- scoping the attach picker to what this client could
        actually attach, rather than every certificate the caller can see.
        Responses carry certificate metadata only, never key material.
      operationId: listClientAttachableCertificates
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      responses:
        '200':
          description: Attachable managed certificates
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AttachableCertificateList'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /clients/{clientId}/routes:
    get:
      tags:
        - Client Attachments
      summary: List routes attached to a client
      operationId: listClientRoutes
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      responses:
        '200':
          description: List of client-route attachments
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ClientRouteAttachment'
        '403':
          $ref: '#/components/responses/Forbidden'
  /clients/{clientId}/routes/attach:
    post:
      tags:
        - Client Attachments
      summary: Attach client to a route (client team initiates)
      operationId: attachFromClient
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/clientId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AttachFromClientRequest'
      responses:
        '201':
          description: Attachment created (pending approval)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientRouteAttachment'
        '403':
          $ref: '#/components/responses/Forbidden'
  /projects/{projectId}/domains/{domainId}/routes/{routeId}/clients:
    get:
      tags:
        - Client Attachments
      summary: List clients attached to a route
      operationId: listRouteClients
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
      responses:
        '200':
          description: List of client-route attachments
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ClientRouteAttachment'
  /projects/{projectId}/domains/{domainId}/routes/{routeId}/clients/attach:
    post:
      tags:
        - Client Attachments
      summary: Attach client to a route (route team initiates)
      operationId: attachFromRoute
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AttachFromRouteRequest'
      responses:
        '201':
          description: Attachment created (pending approval)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientRouteAttachment'
        '403':
          $ref: '#/components/responses/Forbidden'
  /projects/{projectId}/domains/{domainId}/routes/{routeId}/clients/{attachmentId}/detach:
    post:
      tags:
        - Client Attachments
      summary: Request detachment of a client from a route
      operationId: requestDetachFromRoute
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
        - $ref: '#/components/parameters/attachmentId'
      responses:
        '200':
          description: Detachment requested (pending approval)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientRouteAttachment'
        '403':
          $ref: '#/components/responses/Forbidden'
  /projects/{projectId}/domains/{domainId}/routes/{routeId}/effective-ips:
    get:
      tags:
        - Client Attachments
      summary: Get effective IP allowlist for a route
      description: >-
        Returns the merged IP allowlist from all active client attachments with
        IP allowlisting enabled.
      operationId: getEffectiveIPs
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
        - $ref: '#/components/parameters/routeId'
      responses:
        '200':
          description: Effective IP allowlist entries
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/EffectiveIPEntry'
  /projects/{projectId}/client-approvals:
    get:
      tags:
        - Client Approvals
      summary: List client attachment approvals for a project
      operationId: listClientApprovals
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/limit'
        - name: status
          in: query
          schema:
            type: string
            enum:
              - pending
              - approved
              - rejected
            default: pending
      responses:
        '200':
          description: List of client attachment approvals
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalList'
  /projects/{projectId}/client-approvals/{approvalId}:
    get:
      tags:
        - Client Approvals
      summary: Get client attachment approval by ID
      operationId: getClientApproval
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/approvalId'
      responses:
        '200':
          description: Client attachment approval details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Approval'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/client-approvals/{approvalId}/stages/{stageId}/approve:
    post:
      tags:
        - Client Approvals
      summary: Approve a stage of client attachment approval
      operationId: approveClientAttachmentStage
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/approvalId'
        - $ref: '#/components/parameters/stageId'
      responses:
        '200':
          description: Stage approved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Approval'
        '400':
          description: Cannot approve (submitter, already approved, etc.)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/client-approvals/{approvalId}/stages/{stageId}/reject:
    post:
      tags:
        - Client Approvals
      summary: Reject a stage of client attachment approval
      operationId: rejectClientAttachmentStage
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/approvalId'
        - $ref: '#/components/parameters/stageId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RejectRequest'
      responses:
        '200':
          description: Stage rejected
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Approval'
        '400':
          description: Cannot reject
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/approvals:
    get:
      tags:
        - Approvals
      summary: List pending approvals
      operationId: listApprovals
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/limit'
        - name: status
          in: query
          schema:
            type: string
            enum:
              - pending
              - approved
              - rejected
              - cancelled
        - name: entityType
          in: query
          schema:
            type: string
            enum:
              - route
              - client_attachment
      responses:
        '200':
          description: List of approval requests
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalList'
  /projects/{projectId}/approvals/{approvalId}:
    get:
      tags:
        - Approvals
      summary: Get approval request by ID
      operationId: getApproval
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/approvalId'
      responses:
        '200':
          description: Approval request details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Approval'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/approvals/{approvalId}/diff:
    get:
      tags:
        - Approvals
      summary: Get YAML diff for approval request
      operationId: getApprovalDiff
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/approvalId'
      responses:
        '200':
          description: Approval diff with current and proposed YAML
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalDiffResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
  /projects/{projectId}/approvals/{approvalId}/stages/{stageId}/approve:
    post:
      tags:
        - Approvals
      summary: Approve a stage of an approval request
      operationId: approveRequestStage
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/approvalId'
        - $ref: '#/components/parameters/stageId'
      responses:
        '200':
          description: Stage approved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Approval'
        '400':
          description: Cannot approve (submitter, already approved, etc.)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/approvals/{approvalId}/stages/{stageId}/reject:
    post:
      tags:
        - Approvals
      summary: Reject a stage of an approval request
      operationId: rejectRequestStage
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/approvalId'
        - $ref: '#/components/parameters/stageId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RejectRequest'
      responses:
        '200':
          description: Stage rejected
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Approval'
        '400':
          description: Cannot reject
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/approvals/{approvalId}/comments:
    get:
      tags:
        - Approvals
      summary: List comments on an approval
      operationId: listApprovalComments
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/approvalId'
      responses:
        '200':
          description: List of comments
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CommentList'
    post:
      tags:
        - Approvals
      summary: Add comment to an approval
      operationId: createApprovalComment
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/approvalId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCommentRequest'
      responses:
        '201':
          description: Comment created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalComment'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/approvals/{approvalId}/ai-review:
    post:
      tags:
        - Approvals
      summary: Trigger AI review for an approval
      operationId: triggerApprovalAIReview
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/approvalId'
      responses:
        '200':
          description: AI review result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AIReviewResult'
        '409':
          description: AI review already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: AI service unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/approvals/{approvalId}/cancel:
    post:
      tags:
        - Approvals
      summary: Cancel an approval request
      operationId: cancelApproval
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/approvalId'
      responses:
        '200':
          description: Approval cancelled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Approval'
        '400':
          $ref: '#/components/responses/BadRequest'
  /projects/{projectId}/approval-policies:
    get:
      tags:
        - Approval Policies
      summary: List approval policies for a project (Admin only)
      operationId: listApprovalPolicies
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: List of approval policies
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ApprovalPolicy'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags:
        - Approval Policies
      summary: Create approval policy (Admin only)
      description: >
        Creates a custom approval policy for a project. Policies define the

        approval stages required for specific entity types (route,
        client_attachment)

        and actions.
      operationId: createApprovalPolicy
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApprovalPolicyInput'
      responses:
        '201':
          description: Policy created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalPolicy'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
  /projects/{projectId}/approval-policies/{policyId}:
    get:
      tags:
        - Approval Policies
      summary: Get approval policy by ID (Admin only)
      operationId: getApprovalPolicy
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/policyId'
      responses:
        '200':
          description: Approval policy details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalPolicy'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
        - Approval Policies
      summary: Update approval policy (Admin only)
      operationId: updateApprovalPolicy
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/policyId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApprovalPolicyInput'
      responses:
        '200':
          description: Policy updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalPolicy'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
    delete:
      tags:
        - Approval Policies
      summary: Delete approval policy (Admin only)
      operationId: deleteApprovalPolicy
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/policyId'
      responses:
        '200':
          description: Policy deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
  /projects/{projectId}/kubernetes/namespaces:
    get:
      tags:
        - Kubernetes
      summary: List Kubernetes namespaces
      operationId: listNamespaces
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: List of namespaces
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/K8sNamespace'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          description: Cluster discovery failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/kubernetes/gateway-classes:
    get:
      tags:
        - Kubernetes
      summary: List Kubernetes GatewayClasses
      operationId: listGatewayClasses
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: List of GatewayClass names
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          description: Cluster discovery failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/kubernetes/namespaces/{namespace}/services:
    get:
      tags:
        - Kubernetes
      summary: List services in namespace
      operationId: listServices
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - name: namespace
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: List of services
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/K8sService'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          description: Cluster discovery failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/certificates:
    get:
      tags:
        - Managed Certificates
      summary: List managed certificates in project (requires cert.view)
      description: >-
        Each item is enriched with its resolved issuer name/type, Phase 3a
        distribution sync state, and the domains referencing it.
      operationId: listManagedCertificates
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/limit'
        - name: status
          in: query
          required: false
          schema:
            type: string
            enum:
              - pending
              - issuing
              - ready
              - error
          description: Filter by certificate status.
        - name: issuerId
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Filter by issuer.
        - name: usage
          in: query
          required: false
          schema:
            type: string
            enum:
              - server
              - client
          description: Filter by certificate usage.
        - name: expiresBefore
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: Filter to certificates expiring before this RFC3339 timestamp.
      responses:
        '200':
          description: >-
            List of managed certificates, enriched with issuer, distribution,
            and domain info
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EnrichedManagedCertificateList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags:
        - Managed Certificates
      summary: Create managed certificate (requires cert.create)
      description: >-
        Issues a certificate from an issuer granted to this project. Opens an
        approval when the project has approvals enabled (returns 202 with the
        pending certificate + approvalId); otherwise issues immediately (201,
        approvalId null).
      operationId: createManagedCertificate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateManagedCertificateRequest'
      responses:
        '201':
          description: Certificate issued immediately (approvals disabled for this project)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateManagedCertificateResponse'
        '202':
          description: Certificate created and pending approval
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateManagedCertificateResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /projects/{projectId}/certificates/issuers:
    get:
      tags:
        - Managed Certificates
      summary: List certificate issuers granted to this project (requires cert.view)
      description: >-
        Populates a certificate-create form's issuer picker. Never includes key
        material.
      operationId: listProjectCertificateIssuers
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: Certificate issuers granted to this project
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CertificateIssuerList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /projects/{projectId}/certificates/{certificateId}:
    get:
      tags:
        - Managed Certificates
      summary: Get managed certificate (requires cert.view)
      operationId: getManagedCertificate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/certificateId'
      responses:
        '200':
          description: Managed certificate
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManagedCertificate'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - Managed Certificates
      summary: Delete managed certificate (requires cert.delete)
      description: >-
        Removes the leaf `Certificate` CRD (and its Secret) from the control
        cluster.
      operationId: deleteManagedCertificate
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/certificateId'
      responses:
        '204':
          description: Certificate deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/certificates/{certificateId}/status:
    get:
      tags:
        - Managed Certificates
      summary: Get managed certificate issuance status (requires cert.view)
      description: >-
        Live cert-manager readiness of the underlying `Certificate` resource
        (Ready condition + message).
      operationId: getManagedCertificateStatus
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/certificateId'
      responses:
        '200':
          description: Managed certificate status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManagedCertificateStatus'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/certificates/{certificateId}/distribution:
    get:
      tags:
        - Managed Certificates
      summary: Get managed certificate distribution status (requires cert.view)
      description: >-
        Phase 3a distribution state -- how far the certdist controller has
        gotten pushing this certificate's Secret into the project's tenant
        cluster. Returns a pending placeholder (not a 404) when the controller
        has not pushed it yet.
      operationId: getManagedCertificateDistribution
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/certificateId'
      responses:
        '200':
          description: Certificate distribution status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CertificateDistribution'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/certificates/{certificateId}/resync:
    post:
      tags:
        - Managed Certificates
      summary: Force a certificate distribution resync (requires cert.edit)
      description: >-
        Flips the distribution row back to pending so the certdist controller
        re-pushes the certificate's Secret on its next tick. NOTIFY is not wired
        up, so this is a ticker-driven nudge rather than an immediate push.
      operationId: resyncManagedCertificateDistribution
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/certificateId'
      responses:
        '202':
          description: >-
            Resync accepted; the certdist controller will re-push on its next
            tick.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /projects/{projectId}/certificates/{certificateId}/export:
    post:
      tags:
        - Managed Certificates
      summary: Request a certificate export (requires cert.edit)
      description: >
        Opens an approval to export a managed-key certificate's private key
        material.

        Once approved -- immediately, if the project has approvals disabled --
        the

        requesting user gets a short-lived, single-use grant redeemable via

        GET .../export/download. Only applicable to managed-key certificates

        (keyMode: managed): a csr-key-mode certificate's private key never
        existed

        server-side (only its CSR was handed to cert-manager), so there is
        nothing

        to export and this returns 409.
      operationId: requestManagedCertificateExport
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/certificateId'
      responses:
        '202':
          description: >-
            Export approval opened, or (approvals disabled for this project) the
            grant was minted immediately.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RequestExportResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: >-
            Export is not applicable to this certificate (keyMode is csr -- the
            caller already holds the private key).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/certificates/{certificateId}/export/download:
    get:
      tags:
        - Managed Certificates
      summary: Download the exported certificate + private key bundle (one-time)
      description: >
        Streams the certificate's leaf certificate, private key, and issuer CA
        chain

        as a single concatenated `application/x-pem-file` attachment. This is
        the

        ONLY endpoint in the API that ever returns private key material, and
        only

        when the calling user holds an approved, unconsumed, unexpired export
        grant

        for this certificate (see POST .../export) -- the grant is consumed

        atomically on this call, so it can be downloaded exactly once.
      operationId: downloadManagedCertificateExport
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/certificateId'
      responses:
        '200':
          description: >-
            PEM bundle containing the private key, leaf certificate, and CA
            chain, as an attachment download.
          content:
            application/x-pem-file:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '410':
          description: >-
            No approved, unconsumed export grant is available for this user and
            certificate; request a new export and get it approved.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/topology:
    get:
      tags:
        - Topology
      summary: Get the project-wide topology view
      description: |
        Aggregates per-domain summary cards, per-client per-domain rollups, and
        IP-allowlist reachability rows across every domain in the project.
      operationId: getProjectTopology
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      responses:
        '200':
          description: Project topology
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectTopologyResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          description: |
            Topology resource not found. Returned with a deliberately generic
            body (no entity name/ID) to avoid leaking cross-project existence.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/audit:
    get:
      tags:
        - Audit
      summary: List audit logs
      description: Requires audit.view permission, Owner, or Project Admin role.
      operationId: listAuditLogs
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/limit'
        - name: resourceType
          in: query
          schema:
            type: string
            enum:
              - domain
              - domain_template
              - route
              - team
              - user
              - project_namespace
              - approval
        - name: action
          in: query
          schema:
            type: string
            enum:
              - create
              - update
              - delete
              - approve
              - reject
              - deploy
              - approve_stage
              - reject_stage
              - cancel_approval
        - name: userId
          in: query
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: List of audit logs
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditLogList'
  /projects/{projectId}/audit/export:
    get:
      tags:
        - Audit
      summary: Export audit logs as CSV
      description: >
        Exports all matching audit logs as a CSV file. Supports the same filters
        as the list endpoint but returns all results without pagination.
        Requires audit.view permission, Owner, or Project Admin role.
      operationId: exportAuditLogs
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - name: resourceType
          in: query
          schema:
            type: string
            enum:
              - domain
              - domain_template
              - route
              - team
              - user
              - project_namespace
              - approval
        - name: action
          in: query
          schema:
            type: string
            enum:
              - create
              - update
              - delete
              - approve
              - reject
              - deploy
              - approve_stage
              - reject_stage
              - cancel_approval
        - name: userId
          in: query
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: CSV file download
          headers:
            Content-Disposition:
              schema:
                type: string
                example: attachment; filename=audit-log.csv
          content:
            text/csv:
              schema:
                type: string
                description: >
                  CSV with columns: Timestamp, Username, Action, Resource Type,
                  Resource Name, Resource ID, IP Address, Details
        '403':
          $ref: '#/components/responses/Forbidden'
  /projects/{projectId}/audit/cleanup:
    delete:
      tags:
        - Audit
      summary: Delete old audit logs
      description: >
        Permanently deletes audit logs older than the specified number of days.
        This action cannot be undone. Requires Owner or Project Admin role.
      operationId: cleanupAuditLogs
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuditCleanupRequest'
      responses:
        '200':
          description: Cleanup result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditCleanupResponse'
        '400':
          description: Invalid request (days must be >= 1)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          $ref: '#/components/responses/Forbidden'
  /ai/status:
    get:
      tags:
        - AI
      summary: Check AI service availability
      operationId: getAIStatus
      security:
        - bearerAuth: []
      responses:
        '200':
          description: AI status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AIStatus'
  /ai/chat:
    post:
      tags:
        - AI
      summary: Chat with AI assistant (SSE stream)
      description: |
        Interactive AI chat with context about routes and domains. Supports
        conversation history. Returns Server-Sent Events (SSE) stream.
        Enterprise-only feature.
      operationId: aiChat
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AIChatRequest'
      responses:
        '200':
          description: SSE stream of chat response chunks
          content:
            text/event-stream:
              schema:
                type: string
        '400':
          $ref: '#/components/responses/BadRequest'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: AI service unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/domains/{domainId}/ai/generate:
    post:
      tags:
        - AI
      summary: Generate routes using AI (SSE stream)
      operationId: aiGenerateRoutes
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AIGenerateRequest'
      responses:
        '200':
          description: SSE stream of generation chunks
          content:
            text/event-stream:
              schema:
                type: string
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          description: Domain not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: AI service unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /projects/{projectId}/domains/{domainId}/ai/review:
    post:
      tags:
        - AI
      summary: Review route YAML changes using AI
      description: >-
        Sends current and/or proposed YAML configurations to AI for structured
        review. Returns summary, risks, security notes, suggestions, and config
        highlights. Enterprise-only feature.
      operationId: aiReviewYaml
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/projectId'
        - $ref: '#/components/parameters/domainId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AIReviewRequest'
      responses:
        '200':
          description: Structured AI review result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AIReviewResult'
        '400':
          description: Invalid request (missing YAML content)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: AI service unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  parameters:
    page:
      name: page
      in: query
      schema:
        type: integer
        default: 1
        minimum: 1
    limit:
      name: limit
      in: query
      schema:
        type: integer
        default: 20
        minimum: 1
        maximum: 100
    userId:
      name: userId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    projectId:
      name: projectId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    teamId:
      name: teamId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    domainTemplateId:
      name: domainTemplateId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    domainId:
      name: domainId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    namespaceId:
      name: namespaceId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    routeId:
      name: routeId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    clientId:
      name: clientId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    ipId:
      name: ipId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    attachmentId:
      name: attachmentId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    approvalId:
      name: approvalId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    stageId:
      name: stageId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    headerId:
      name: headerId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    policyId:
      name: policyId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    tokenId:
      name: tokenId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    dnsCredentialId:
      name: dnsCredentialId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    issuerId:
      name: issuerId
      in: path
      required: true
      schema:
        type: string
        format: uuid
    certificateId:
      name: certificateId
      in: path
      required: true
      schema:
        type: string
        format: uuid
  responses:
    Unauthorized:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    BadRequest:
      description: Bad request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: Forbidden
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
        message:
          type: string
    Pagination:
      type: object
      properties:
        page:
          type: integer
        limit:
          type: integer
        total:
          type: integer
        totalPages:
          type: integer
    LoginRequest:
      type: object
      required:
        - username
        - password
      properties:
        username:
          type: string
        password:
          type: string
    LoginResponse:
      type: object
      required:
        - accessToken
        - refreshToken
        - expiresAt
      properties:
        accessToken:
          type: string
        refreshToken:
          type: string
        expiresAt:
          type: string
          format: date-time
        user:
          $ref: '#/components/schemas/User'
    RefreshTokenRequest:
      type: object
      required:
        - refreshToken
      properties:
        refreshToken:
          type: string
    ChangePasswordRequest:
      type: object
      required:
        - currentPassword
        - newPassword
      properties:
        currentPassword:
          type: string
          description: The user's current password
        newPassword:
          type: string
          minLength: 8
          description: The new password (minimum 8 characters)
    CreateApiTokenRequest:
      type: object
      required:
        - name
      properties:
        name:
          type: string
        expiresAt:
          type: string
          format: date-time
    ApiToken:
      type: object
      properties:
        id:
          type: string
          format: uuid
        userId:
          type: string
          format: uuid
        name:
          type: string
        lastUsedAt:
          type: string
          format: date-time
        expiresAt:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time
    ApiTokenCreated:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        token:
          type: string
          description: Full token (only shown once)
        expiresAt:
          type: string
          format: date-time
    User:
      type: object
      properties:
        id:
          type: string
          format: uuid
        username:
          type: string
        email:
          type: string
          format: email
        role:
          type: string
          enum:
            - owner
            - user
        isActive:
          type: boolean
        authProvider:
          type: string
          description: How the user authenticates (e.g., local, sso)
        providerSubject:
          type: string
          nullable: true
          description: Subject identifier from the SSO provider, if applicable
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    CurrentUser:
      description: >-
        User profile returned by GET /auth/me, including how the caller
        authenticated this request
      allOf:
        - $ref: '#/components/schemas/User'
        - type: object
          properties:
            authMethod:
              type: string
              description: How the current request was authenticated (e.g., jwt, api_token)
    UserList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/User'
        pagination:
          $ref: '#/components/schemas/Pagination'
    CreateUserRequest:
      type: object
      required:
        - username
        - email
        - password
        - role
      properties:
        username:
          type: string
        email:
          type: string
          format: email
        password:
          type: string
        role:
          type: string
          enum:
            - owner
            - user
    UpdateUserRequest:
      type: object
      properties:
        email:
          type: string
          format: email
        password:
          type: string
        isActive:
          type: boolean
    Team:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        memberCount:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    TeamList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Team'
        pagination:
          $ref: '#/components/schemas/Pagination'
    CreateTeamRequest:
      type: object
      required:
        - name
      properties:
        name:
          type: string
        description:
          type: string
    UpdateTeamRequest:
      type: object
      properties:
        name:
          type: string
        description:
          type: string
    AddTeamMemberRequest:
      type: object
      required:
        - userId
      properties:
        userId:
          type: string
          format: uuid
    AddMemberByEmailRequest:
      type: object
      required:
        - email
      properties:
        email:
          type: string
          format: email
    AddMemberResult:
      type: object
      properties:
        type:
          type: string
          enum:
            - added
            - invited
        user:
          $ref: '#/components/schemas/User'
        invite:
          $ref: '#/components/schemas/TeamEmailInvite'
    TeamEmailInvite:
      type: object
      properties:
        id:
          type: string
          format: uuid
        teamId:
          type: string
          format: uuid
        email:
          type: string
        invitedBy:
          type: string
          format: uuid
        createdAt:
          type: string
          format: date-time
        team:
          $ref: '#/components/schemas/Team'
        inviter:
          $ref: '#/components/schemas/User'
    Project:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        connectionType:
          type: string
          enum:
            - in_cluster
            - kubeconfig
            - api_token
          description: Kubernetes connection type
        k8sApiUrl:
          type: string
        k8sTlsSkipVerify:
          type: boolean
          description: Whether TLS verification of the K8s API server is skipped
        isConnected:
          type: boolean
        lastConnectedAt:
          type: string
          format: date-time
        createdBy:
          type: string
          format: uuid
        domainCount:
          type: integer
        routeCount:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        labels:
          $ref: '#/components/schemas/Labels'
        approvalEnabled:
          type: boolean
          description: Whether route changes in this project require approval
        selfApprovalAllowed:
          type: boolean
          description: Whether the author of a change may also approve it
        metricsEndpointUrl:
          type: string
          description: Prometheus/VictoriaMetrics-compatible metrics endpoint URL
        metricsAuthType:
          type: string
          enum:
            - none
            - bearer
            - basic
          description: Authentication method for the metrics endpoint
        metricsUsername:
          type: string
          description: Username for basic auth against the metrics endpoint
        metricsTlsSkipVerify:
          type: boolean
          description: Whether TLS verification of the metrics endpoint is skipped
        metricsCaCert:
          type: string
          description: Custom CA certificate PEM for the metrics endpoint
    ProjectList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Project'
        pagination:
          $ref: '#/components/schemas/Pagination'
    CreateProjectRequest:
      type: object
      required:
        - name
      properties:
        name:
          type: string
        description:
          type: string
        connectionType:
          type: string
          enum:
            - in_cluster
            - kubeconfig
            - api_token
          description: >-
            Kubernetes connection type. The wire values use underscores (e.g.
            `in_cluster`, `api_token`), not hyphens.
        kubeconfig:
          type: string
          description: Kubeconfig content (for connectionType=kubeconfig)
        k8sApiUrl:
          type: string
          description: Kubernetes API URL (for connectionType=api_token)
        k8sToken:
          type: string
          description: Kubernetes bearer token (for connectionType=api_token)
        tlsVerification:
          type: string
          enum:
            - system_ca
            - custom_ca
            - skip
          description: >-
            TLS verification mode for the K8s API (connectionType=api_token
            only). Defaults to `skip` when omitted.
        k8sCaCert:
          type: string
          description: Custom CA certificate PEM (required when tlsVerification=custom_ca)
        labels:
          $ref: '#/components/schemas/Labels'
    UpdateProjectRequest:
      type: object
      properties:
        name:
          type: string
        description:
          type: string
        kubeconfig:
          type: string
          description: Kubeconfig content (for connectionType=kubeconfig)
        k8sApiUrl:
          type: string
          description: Kubernetes API URL (for connectionType=api_token)
        k8sToken:
          type: string
          description: Kubernetes bearer token (for connectionType=api_token)
        tlsVerification:
          type: string
          enum:
            - system_ca
            - custom_ca
            - skip
          description: >-
            TLS verification mode for the K8s API (connectionType=api_token
            only)
        k8sCaCert:
          type: string
          description: Custom CA certificate PEM (required when tlsVerification=custom_ca)
        labels:
          $ref: '#/components/schemas/Labels'
        approvalEnabled:
          type: boolean
          description: Whether route changes in this project require approval
        selfApprovalAllowed:
          type: boolean
          description: Whether the author of a change may also approve it
        metricsEndpointUrl:
          type: string
          description: Prometheus/VictoriaMetrics-compatible metrics endpoint URL
        metricsAuthType:
          type: string
          enum:
            - none
            - bearer
            - basic
          description: Authentication method for the metrics endpoint
        metricsUsername:
          type: string
          description: Username for basic auth against the metrics endpoint
        metricsPassword:
          type: string
          description: >-
            Password for basic auth against the metrics endpoint. Write-only;
            encrypted at rest and never returned.
        metricsToken:
          type: string
          description: >-
            Bearer token for the metrics endpoint. Write-only; encrypted at rest
            and never returned.
        metricsTlsSkipVerify:
          type: boolean
          description: Whether TLS verification of the metrics endpoint is skipped
        metricsCaCert:
          type: string
          description: Custom CA certificate PEM for the metrics endpoint
    ConnectionTestResult:
      type: object
      properties:
        success:
          type: boolean
        message:
          type: string
        kubernetesVersion:
          type: string
    ProjectPermissions:
      type: object
      properties:
        canManageDomainTemplates:
          type: boolean
        canManageDomains:
          type: boolean
        canManageTeams:
          type: boolean
        canCreateRoutes:
          type: boolean
        canApproveRoutes:
          type: boolean
        canViewAudit:
          type: boolean
        permissions:
          type: array
          items:
            type: string
        isOwner:
          type: boolean
        isProjectAdmin:
          type: boolean
    AddProjectAdminRequest:
      type: object
      required:
        - userId
      properties:
        userId:
          type: string
          format: uuid
    ProjectCapabilities:
      type: object
      description: Available capabilities for a project based on cluster configuration
      properties:
        rateLimitAvailable:
          type: boolean
          description: >-
            Whether rate limiting is available (requires Redis backend in Envoy
            Gateway)
    ProjectTeamRole:
      type: object
      properties:
        id:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        teamId:
          type: string
          format: uuid
        presets:
          type: array
          items:
            $ref: '#/components/schemas/ProjectTeamPreset'
        effectivePermissions:
          type: array
          items:
            type: string
        team:
          $ref: '#/components/schemas/Team'
        createdAt:
          type: string
          format: date-time
    ProjectTeamPreset:
      type: object
      properties:
        id:
          type: string
          format: uuid
        projectTeamRoleId:
          type: string
          format: uuid
        presetId:
          type: string
          format: uuid
        preset:
          $ref: '#/components/schemas/PermissionPreset'
        createdAt:
          type: string
          format: date-time
    AssignTeamRequest:
      type: object
      required:
        - teamId
        - presetIds
      properties:
        teamId:
          type: string
          format: uuid
        presetIds:
          type: array
          minItems: 1
          items:
            type: string
            format: uuid
    UpdateTeamPresetsInput:
      type: object
      required:
        - presetIds
      properties:
        presetIds:
          type: array
          minItems: 1
          items:
            type: string
            format: uuid
    Permission:
      type: string
      enum:
        - route.view
        - route.create
        - route.edit
        - route.delete
        - route.deploy
        - route.approve
        - client.view
        - client.create
        - client.edit
        - client.delete
        - client.manage_ip
        - client.manage_apikey
        - client.manage_jwt
        - client.attach
        - client.detach
        - client.approve
        - domain.view
        - domain.create
        - domain.edit
        - domain.delete
        - project.settings
        - project.teams
        - project.approval_policy
        - audit.view
    PermissionPreset:
      type: object
      properties:
        id:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        permissions:
          type: array
          items:
            $ref: '#/components/schemas/Permission'
        isBuiltin:
          type: boolean
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    CreatePresetRequest:
      type: object
      required:
        - name
        - permissions
      properties:
        name:
          type: string
        description:
          type: string
        permissions:
          type: array
          items:
            $ref: '#/components/schemas/Permission'
    UpdatePresetRequest:
      type: object
      properties:
        name:
          type: string
        description:
          type: string
        permissions:
          type: array
          items:
            $ref: '#/components/schemas/Permission'
    DomainTemplate:
      type: object
      properties:
        id:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        controllerName:
          type: string
        exposureType:
          type: string
          enum:
            - LoadBalancer
            - ClusterIP
        tlsMode:
          type: string
          enum:
            - tls_only
            - no_tls
            - both
        httpPort:
          type: integer
        httpsPort:
          type: integer
        tlsPolicy:
          type: string
          enum:
            - terminate
            - passthrough
        externalTrafficPolicy:
          type: string
          enum:
            - Cluster
            - Local
        loadBalancerClass:
          type: string
        annotations:
          type: object
          additionalProperties:
            type: string
        podAnnotations:
          type: object
          additionalProperties:
            type: string
          description: Annotations applied to the Envoy proxy pods
        containerResources:
          $ref: '#/components/schemas/ContainerResourcesConfig'
        scalingConfig:
          $ref: '#/components/schemas/ScalingConfig'
        mergeGateways:
          type: boolean
          description: >-
            Merge all Gateways using this template into a single Envoy
            Deployment/Service
        telemetryAccessLog:
          $ref: '#/components/schemas/TelemetryAccessLogConfig'
        telemetryTracing:
          $ref: '#/components/schemas/TelemetryTracingConfig'
        telemetryMetrics:
          $ref: '#/components/schemas/TelemetryMetricsConfig'
        podPlacement:
          $ref: '#/components/schemas/PodPlacementConfig'
        pdbConfig:
          $ref: '#/components/schemas/PDBConfig'
        deploymentStrategy:
          $ref: '#/components/schemas/DeploymentStrategyConfig'
        status:
          type: string
          enum:
            - pending
            - active
            - error
        statusMessage:
          type: string
        k8sGatewayClassName:
          type: string
        k8sEnvoyProxyName:
          type: string
        createdBy:
          type: string
          format: uuid
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    DomainTemplateList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/DomainTemplate'
        pagination:
          $ref: '#/components/schemas/Pagination'
    CreateDomainTemplateRequest:
      type: object
      required:
        - name
        - exposureType
        - tlsMode
      properties:
        name:
          type: string
        description:
          type: string
        controllerName:
          type: string
          default: gateway.envoyproxy.io/gatewayclass-controller
        exposureType:
          type: string
          enum:
            - LoadBalancer
            - ClusterIP
        tlsMode:
          type: string
          enum:
            - tls_only
            - no_tls
            - both
        httpPort:
          type: integer
          default: 80
        httpsPort:
          type: integer
          default: 443
        tlsPolicy:
          type: string
          enum:
            - terminate
            - passthrough
          default: terminate
        externalTrafficPolicy:
          type: string
          enum:
            - Cluster
            - Local
        loadBalancerClass:
          type: string
        annotations:
          type: object
          additionalProperties:
            type: string
        podAnnotations:
          type: object
          additionalProperties:
            type: string
          description: Annotations applied to the Envoy proxy pods
        containerResources:
          $ref: '#/components/schemas/ContainerResourcesConfig'
        scalingConfig:
          $ref: '#/components/schemas/ScalingConfig'
        mergeGateways:
          type: boolean
          description: >-
            Merge all Gateways using this template into a single Envoy
            Deployment/Service
        telemetryAccessLog:
          $ref: '#/components/schemas/TelemetryAccessLogConfig'
        telemetryTracing:
          $ref: '#/components/schemas/TelemetryTracingConfig'
        telemetryMetrics:
          $ref: '#/components/schemas/TelemetryMetricsConfig'
        podPlacement:
          $ref: '#/components/schemas/PodPlacementConfig'
        pdbConfig:
          $ref: '#/components/schemas/PDBConfig'
        deploymentStrategy:
          $ref: '#/components/schemas/DeploymentStrategyConfig'
    UpdateDomainTemplateRequest:
      type: object
      properties:
        description:
          type: string
        externalTrafficPolicy:
          type: string
          enum:
            - Cluster
            - Local
        loadBalancerClass:
          type: string
        annotations:
          type: object
          additionalProperties:
            type: string
        podAnnotations:
          type: object
          additionalProperties:
            type: string
          description: Annotations applied to the Envoy proxy pods
        containerResources:
          $ref: '#/components/schemas/ContainerResourcesConfig'
        scalingConfig:
          $ref: '#/components/schemas/ScalingConfig'
        telemetryAccessLog:
          $ref: '#/components/schemas/TelemetryAccessLogConfig'
        telemetryTracing:
          $ref: '#/components/schemas/TelemetryTracingConfig'
        telemetryMetrics:
          $ref: '#/components/schemas/TelemetryMetricsConfig'
        podPlacement:
          $ref: '#/components/schemas/PodPlacementConfig'
        pdbConfig:
          $ref: '#/components/schemas/PDBConfig'
        deploymentStrategy:
          $ref: '#/components/schemas/DeploymentStrategyConfig'
        clearTelemetryAccessLog:
          type: boolean
          description: >-
            When true, clears the stored telemetryAccessLog config (JSON null is
            indistinguishable from absent)
        clearTelemetryTracing:
          type: boolean
          description: When true, clears the stored telemetryTracing config
        clearTelemetryMetrics:
          type: boolean
          description: When true, clears the stored telemetryMetrics config
        clearPodPlacement:
          type: boolean
          description: When true, clears the stored podPlacement config
        clearPdbConfig:
          type: boolean
          description: When true, clears the stored pdbConfig config
        clearDeploymentStrategy:
          type: boolean
          description: When true, clears the stored deploymentStrategy config
    DomainTemplateManifests:
      type: object
      properties:
        gatewayClassYaml:
          type: string
        envoyProxyYaml:
          type: string
    DomainTemplateCreatePreviewRequest:
      type: object
      required:
        - name
        - exposureType
        - tlsMode
      properties:
        name:
          type: string
        description:
          type: string
        controllerName:
          type: string
        exposureType:
          type: string
          enum:
            - LoadBalancer
            - ClusterIP
        tlsMode:
          type: string
          enum:
            - tls_only
            - no_tls
            - both
        httpPort:
          type: integer
        httpsPort:
          type: integer
        tlsPolicy:
          type: string
          enum:
            - terminate
            - passthrough
        externalTrafficPolicy:
          type: string
          enum:
            - Cluster
            - Local
        loadBalancerClass:
          type: string
        annotations:
          type: object
          additionalProperties:
            type: string
        podAnnotations:
          type: object
          additionalProperties:
            type: string
        containerResources:
          $ref: '#/components/schemas/ContainerResourcesConfig'
        scalingConfig:
          $ref: '#/components/schemas/ScalingConfig'
        mergeGateways:
          type: boolean
          description: >-
            Merge all Gateways using this template into a single Envoy
            Deployment/Service
        telemetryAccessLog:
          $ref: '#/components/schemas/TelemetryAccessLogConfig'
        telemetryTracing:
          $ref: '#/components/schemas/TelemetryTracingConfig'
        telemetryMetrics:
          $ref: '#/components/schemas/TelemetryMetricsConfig'
        podPlacement:
          $ref: '#/components/schemas/PodPlacementConfig'
        pdbConfig:
          $ref: '#/components/schemas/PDBConfig'
        deploymentStrategy:
          $ref: '#/components/schemas/DeploymentStrategyConfig'
        includeAIReview:
          type: boolean
        changeDescription:
          type: string
    DomainTemplateCreatePreviewResult:
      type: object
      properties:
        gatewayClassYaml:
          type: string
        envoyProxyYaml:
          type: string
        gatewayYaml:
          type: string
          description: Example Gateway manifest showing TLS configuration impact
        aiReview:
          $ref: '#/components/schemas/AIReviewResult'
    DomainTemplatePreviewChangesRequest:
      type: object
      properties:
        description:
          type: string
        externalTrafficPolicy:
          type: string
          enum:
            - Cluster
            - Local
        loadBalancerClass:
          type: string
        annotations:
          type: object
          additionalProperties:
            type: string
        podAnnotations:
          type: object
          additionalProperties:
            type: string
        containerResources:
          $ref: '#/components/schemas/ContainerResourcesConfig'
        scalingConfig:
          $ref: '#/components/schemas/ScalingConfig'
        telemetryAccessLog:
          $ref: '#/components/schemas/TelemetryAccessLogConfig'
        telemetryTracing:
          $ref: '#/components/schemas/TelemetryTracingConfig'
        telemetryMetrics:
          $ref: '#/components/schemas/TelemetryMetricsConfig'
        podPlacement:
          $ref: '#/components/schemas/PodPlacementConfig'
        pdbConfig:
          $ref: '#/components/schemas/PDBConfig'
        deploymentStrategy:
          $ref: '#/components/schemas/DeploymentStrategyConfig'
        clearTelemetryAccessLog:
          type: boolean
          description: >-
            When true, clears the stored telemetryAccessLog config (JSON null is
            indistinguishable from absent)
        clearTelemetryTracing:
          type: boolean
          description: When true, clears the stored telemetryTracing config
        clearTelemetryMetrics:
          type: boolean
          description: When true, clears the stored telemetryMetrics config
        clearPodPlacement:
          type: boolean
          description: When true, clears the stored podPlacement config
        clearPdbConfig:
          type: boolean
          description: When true, clears the stored pdbConfig config
        clearDeploymentStrategy:
          type: boolean
          description: When true, clears the stored deploymentStrategy config
        includeAIReview:
          type: boolean
        changeDescription:
          type: string
    DomainTemplatePreviewResult:
      type: object
      properties:
        currentEnvoyProxyYaml:
          type: string
        proposedEnvoyProxyYaml:
          type: string
        aiReview:
          $ref: '#/components/schemas/AIReviewResult'
    ContainerResourcesConfig:
      type: object
      description: Container resource requests and limits for the Envoy proxy container
      properties:
        requests:
          $ref: '#/components/schemas/ResourceValues'
        limits:
          $ref: '#/components/schemas/ResourceValues'
    ResourceValues:
      type: object
      description: >-
        CPU and memory quantities (Kubernetes resource quantity strings, e.g.
        "500m", "256Mi")
      properties:
        cpu:
          type: string
        memory:
          type: string
    ScalingConfig:
      type: object
      description: Scaling configuration - fixed replica count, or HPA-managed
      properties:
        type:
          type: string
          enum:
            - fixed
            - hpa
        replicas:
          type: integer
          format: int32
          description: Replica count when type=fixed
        minReplicas:
          type: integer
          format: int32
          description: Minimum replicas when type=hpa
        maxReplicas:
          type: integer
          format: int32
          description: Maximum replicas when type=hpa
    TelemetryAccessLogConfig:
      type: object
      description: Access log configuration (spec.telemetry.accessLog)
      properties:
        format:
          $ref: '#/components/schemas/TelemetryAccessLogFormat'
        sink:
          $ref: '#/components/schemas/TelemetryAccessLogSink'
    TelemetryAccessLogFormat:
      type: object
      description: Access log format - the type-specific body is populated based on "type"
      properties:
        type:
          type: string
          enum:
            - text
            - json
            - disabled
        text:
          type: string
          description: Log format text template (present when type=text)
        json:
          type: object
          additionalProperties:
            type: string
          description: Log format JSON field map (present when type=json)
    TelemetryAccessLogSink:
      type: object
      description: Access log sink - the type-specific body is populated based on "type"
      properties:
        type:
          type: string
          enum:
            - file
            - otel
        file:
          $ref: '#/components/schemas/TelemetryAccessLogFileSink'
        otel:
          $ref: '#/components/schemas/TelemetryAccessLogOTelSink'
    TelemetryAccessLogFileSink:
      type: object
      description: File sink body (present when sink type=file)
      properties:
        path:
          type: string
          description: Must be /dev/stdout or /dev/stderr
    TelemetryAccessLogOTelSink:
      type: object
      description: >-
        OpenTelemetry sink body referencing an in-cluster Service (present when
        sink type=otel)
      properties:
        namespace:
          type: string
        service:
          type: string
        port:
          type: integer
          format: int32
    TelemetryTracingConfig:
      type: object
      description: Tracing configuration (spec.telemetry.tracing)
      properties:
        samplingRate:
          type: number
          format: double
        provider:
          $ref: '#/components/schemas/TelemetryServiceRef'
        customTags:
          type: array
          items:
            $ref: '#/components/schemas/TelemetryTracingTag'
    TelemetryServiceRef:
      type: object
      description: Reference to an in-cluster Kubernetes Service
      properties:
        namespace:
          type: string
        service:
          type: string
        port:
          type: integer
          format: int32
    TelemetryTracingTag:
      type: object
      description: >-
        One custom tracing tag. "value" is populated when type=literal; "header"
        (and optional "defaultValue") when type=requestHeader.
      properties:
        type:
          type: string
          enum:
            - literal
            - requestHeader
        tag:
          type: string
        value:
          type: string
        header:
          type: string
        defaultValue:
          type: string
    TelemetryMetricsConfig:
      type: object
      description: Metrics configuration (spec.telemetry.metrics)
      properties:
        prometheus:
          $ref: '#/components/schemas/TelemetryPrometheusConfig'
        enableVirtualHostStats:
          type: boolean
        enablePerEndpointStats:
          type: boolean
        sinks:
          type: array
          items:
            $ref: '#/components/schemas/TelemetryMetricsSink'
    TelemetryPrometheusConfig:
      type: object
      properties:
        disable:
          type: boolean
    TelemetryMetricsSink:
      type: object
      description: >-
        An additional metrics sink. Currently only "openTelemetry" is supported,
        with at most one entry.
      properties:
        type:
          type: string
          enum:
            - openTelemetry
        namespace:
          type: string
        service:
          type: string
        port:
          type: integer
          format: int32
    PodPlacementConfig:
      type: object
      properties:
        nodeSelector:
          type: object
          additionalProperties:
            type: string
        tolerations:
          type: array
          items:
            $ref: '#/components/schemas/TolerationConfig'
        topologySpreadConstraints:
          type: array
          items:
            $ref: '#/components/schemas/TopologySpreadConstraintConfig'
        priorityClassName:
          type: string
    TolerationConfig:
      type: object
      properties:
        key:
          type: string
        operator:
          type: string
        value:
          type: string
        effect:
          type: string
        tolerationSeconds:
          type: integer
          format: int64
    TopologySpreadConstraintConfig:
      type: object
      properties:
        maxSkew:
          type: integer
          format: int32
        topologyKey:
          type: string
        whenUnsatisfiable:
          type: string
    PDBConfig:
      type: object
      description: >-
        PodDisruptionBudget configuration - exactly one of
        minAvailable/maxUnavailable, selected by "kind"
      properties:
        kind:
          type: string
          enum:
            - minAvailable
            - maxUnavailable
        value:
          type: string
          description: Absolute number or percentage (e.g. "1", "50%")
    DeploymentStrategyConfig:
      type: object
      properties:
        type:
          type: string
          enum:
            - RollingUpdate
            - Recreate
        rollingUpdate:
          $ref: '#/components/schemas/RollingUpdateConfig'
    RollingUpdateConfig:
      type: object
      description: >-
        Present when type=RollingUpdate; omitted sub-fields let Kubernetes
        defaults apply
      properties:
        maxSurge:
          type: string
          description: Absolute number or percentage (e.g. "1", "25%")
        maxUnavailable:
          type: string
          description: Absolute number or percentage (e.g. "1", "25%")
    ProjectNamespace:
      type: object
      properties:
        id:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        namespace:
          type: string
        capabilities:
          type: array
          description: Roles this namespace can play in the project.
          items:
            $ref: '#/components/schemas/NamespaceCapability'
        referenceGrantCreated:
          type: boolean
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    CreateProjectNamespaceRequest:
      type: object
      required:
        - namespace
        - capabilities
      properties:
        namespace:
          type: string
        capabilities:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/NamespaceCapability'
    Domain:
      type: object
      properties:
        id:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        domainTemplateId:
          type: string
          format: uuid
        name:
          type: string
        hostname:
          type: string
        httpPort:
          type: integer
        httpsPort:
          type: integer
        tlsMode:
          type: string
          enum:
            - tls_only
            - no_tls
            - both
        tlsSecretName:
          type: string
        tlsSecretNamespace:
          type: string
          description: >-
            Kubernetes namespace containing the TLS secret (defaults to the
            project's managed namespace)
        tlsPolicy:
          type: string
          enum:
            - terminate
            - passthrough
        namespace:
          type: string
          description: Kubernetes namespace for gateway resources
        k8sGatewayName:
          type: string
          description: Name of the Kubernetes Gateway resource
        k8sGatewayClassName:
          type: string
          description: Name of the GatewayClass
        status:
          type: string
          enum:
            - pending
            - active
            - error
        statusMessage:
          type: string
        createdBy:
          type: string
          format: uuid
        routeCount:
          type: integer
        labels:
          $ref: '#/components/schemas/Labels'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    DomainList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Domain'
        pagination:
          $ref: '#/components/schemas/Pagination'
    CreateDomainRequest:
      type: object
      required:
        - name
        - hostname
        - domainTemplateId
      properties:
        name:
          type: string
        hostname:
          type: string
        domainTemplateId:
          type: string
          format: uuid
        tlsSecretName:
          type: string
        tlsSecretNamespace:
          type: string
          description: >-
            Kubernetes namespace containing the TLS secret. Must be managed by
            the project unless it is the default FastGateway namespace.
        namespace:
          type: string
          description: >-
            Kubernetes namespace to deploy the domain's gateway resources into.
            Defaults to the default FastGateway namespace; any other value must
            be registered for this project.
        labels:
          $ref: '#/components/schemas/Labels'
    UpdateDomainRequest:
      type: object
      properties:
        name:
          type: string
        tlsSecretName:
          type: string
        tlsSecretNamespace:
          type: string
          description: >-
            Kubernetes namespace containing the TLS secret. Must be managed by
            the project unless it is the default FastGateway namespace.
        labels:
          $ref: '#/components/schemas/Labels'
    DomainSettings:
      type: object
      description: >
        Domain-level settings (gateway-agnostic configuration), plus the
        domain's

        BackendTrafficPolicy and EnvoyExtensionPolicy configuration. Always
        returned

        as an object; fields are omitted (not null) when not configured.
      properties:
        id:
          type: string
          format: uuid
        domainId:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        settings:
          $ref: '#/components/schemas/DomainSettingsConfig'
        backendTrafficPolicy:
          $ref: '#/components/schemas/BackendTrafficPolicyConfig'
        extensionPolicy:
          $ref: '#/components/schemas/EnvoyExtensionPolicyConfig'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    DomainSettingsRecord:
      type: object
      description: >-
        Stored domain settings record, as returned directly by the mTLS CA
        endpoints (no BackendTrafficPolicy/EnvoyExtensionPolicy attached).
      properties:
        id:
          type: string
          format: uuid
        domainId:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        settings:
          $ref: '#/components/schemas/DomainSettingsConfig'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    UpdateDomainSettingsResponse:
      type: object
      nullable: true
      description: >
        Domain settings written by updateDomainSettings, with any
        operator-facing

        warnings generated while applying the change (e.g. mTLS enabled with no
        CA

        configured). Null when the update disabled every CTP/BTP/extension
        setting

        for this domain.
      properties:
        id:
          type: string
          format: uuid
        domainId:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        settings:
          $ref: '#/components/schemas/DomainSettingsConfig'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        warnings:
          type: array
          items:
            type: string
          description: >-
            Non-fatal, operator-facing warnings generated while writing the
            settings
    DomainSettingsConfig:
      type: object
      description: Gateway-agnostic domain configuration
      properties:
        clientConnection:
          $ref: '#/components/schemas/ClientConnectionConfig'
        clientIPDetection:
          $ref: '#/components/schemas/ClientIPDetectionConfig'
        timeout:
          $ref: '#/components/schemas/DomainTimeoutConfig'
        http3:
          $ref: '#/components/schemas/HTTP3Config'
        tls:
          $ref: '#/components/schemas/TLSSettingsConfig'
        mtls:
          $ref: '#/components/schemas/DomainMTLSConfig'
    ClientConnectionConfig:
      type: object
      description: Client connection settings
      properties:
        tcpKeepalive:
          $ref: '#/components/schemas/TCPKeepaliveConfig'
        proxyProtocol:
          $ref: '#/components/schemas/ProxyProtocolConfig'
        connectionLimit:
          $ref: '#/components/schemas/ConnectionLimitConfig'
        bufferLimit:
          type: string
          description: Buffer size limit (e.g., "32Ki")
    TCPKeepaliveConfig:
      type: object
      description: TCP keepalive configuration for downstream client connections
      properties:
        probes:
          type: integer
          format: int32
          description: >-
            Maximum number of keepalive probes before considering the connection
            dead
          example: 3
        idleTime:
          type: string
          description: Duration of idle time before keepalive probes start (e.g., "60s")
          example: 60s
        interval:
          type: string
          description: Duration between keepalive probes (e.g., "10s")
          example: 10s
    ClientIPDetectionConfig:
      type: object
      description: >-
        Client IP detection configuration for extracting client IP from HTTP
        headers
      properties:
        xForwardedFor:
          $ref: '#/components/schemas/XForwardedForConfig'
        customHeader:
          $ref: '#/components/schemas/CustomHeaderConfig'
    XForwardedForConfig:
      type: object
      description: Extract client IP from X-Forwarded-For header
      required:
        - numTrustedHops
      properties:
        numTrustedHops:
          type: integer
          minimum: 1
          maximum: 10
          description: Number of trusted proxy hops in the XFF chain
    CustomHeaderConfig:
      type: object
      description: Extract client IP from a custom header
      required:
        - name
      properties:
        name:
          type: string
          description: Name of the header containing client IP (e.g., CF-Connecting-IP)
        failClosed:
          type: boolean
          default: false
          description: Reject requests if the header is missing
    UpdateDomainSettingsRequest:
      type: object
      description: >-
        Request to update domain settings. Omit or null fields to disable
        features.
      properties:
        clientConnection:
          $ref: '#/components/schemas/ClientConnectionConfig'
        clientIPDetection:
          $ref: '#/components/schemas/ClientIPDetectionConfig'
        timeout:
          $ref: '#/components/schemas/DomainTimeoutConfig'
        http3:
          $ref: '#/components/schemas/HTTP3Config'
        tls:
          $ref: '#/components/schemas/TLSSettingsConfig'
        mtls:
          $ref: '#/components/schemas/DomainMTLSConfig'
        backendTrafficPolicy:
          $ref: '#/components/schemas/BackendTrafficPolicyConfig'
        extensionPolicy:
          $ref: '#/components/schemas/EnvoyExtensionPolicyConfig'
    ProxyProtocolConfig:
      type: object
      description: Proxy protocol configuration
      properties:
        enabled:
          type: boolean
          description: Enable proxy protocol
    ConnectionLimitConfig:
      type: object
      description: Connection limit configuration
      properties:
        maxConnections:
          type: integer
          description: Maximum number of connections
        closeDelay:
          type: string
          description: Delay before closing connections (e.g., "5s")
        maxConnectionDuration:
          type: string
          description: Maximum connection duration (e.g., "1h")
        maxRequestsPerConnection:
          type: integer
          description: Maximum requests per connection
    DomainTimeoutConfig:
      type: object
      description: Domain-level timeout configuration
      properties:
        http:
          $ref: '#/components/schemas/HTTPDomainTimeoutConfig'
    HTTPDomainTimeoutConfig:
      type: object
      description: HTTP timeout settings
      properties:
        requestReceivedTimeout:
          type: string
          description: Timeout for receiving the complete request (e.g., "30s")
        idleTimeout:
          type: string
          description: Idle connection timeout (e.g., "120s")
    HTTP3Config:
      type: object
      description: HTTP/3 configuration
      properties:
        enabled:
          type: boolean
          description: Enable HTTP/3
    TLSSettingsConfig:
      type: object
      description: TLS settings configuration
      properties:
        minVersion:
          type: string
          enum:
            - TLS1.0
            - TLSv1.0
            - TLS1.1
            - TLSv1.1
            - TLS1.2
            - TLSv1.2
            - TLS1.3
            - TLSv1.3
            - Auto
            - ''
          description: Minimum TLS version
        maxVersion:
          type: string
          enum:
            - TLS1.0
            - TLSv1.0
            - TLS1.1
            - TLSv1.1
            - TLS1.2
            - TLSv1.2
            - TLS1.3
            - TLSv1.3
            - Auto
            - ''
          description: Maximum TLS version
        ciphers:
          type: array
          items:
            type: string
          description: TLS cipher suites
        ecdhCurves:
          type: array
          items:
            type: string
          description: ECDH curves
        signatureAlgorithms:
          type: array
          items:
            type: string
          description: Signature algorithms
    DomainMTLSConfig:
      type: object
      description: Domain-level mTLS configuration
      properties:
        enabled:
          type: boolean
          description: Enable mTLS
        optional:
          type: boolean
          description: Make client certificate optional
        caCerts:
          type: array
          items:
            $ref: '#/components/schemas/MTLSCACert'
          description: CA certificates for client verification
        sanWhitelist:
          type: array
          items:
            $ref: '#/components/schemas/MTLSSANEntry'
          description: Subject Alternative Name whitelist
        hashWhitelist:
          type: array
          items:
            type: string
          description: Certificate hash whitelist
    MTLSCACert:
      type: object
      description: mTLS CA certificate reference
      properties:
        id:
          type: string
        name:
          type: string
          description: CA certificate display name
        secretName:
          type: string
          description: K8s Secret name containing the CA cert
        secretKey:
          type: string
          description: Key within the Secret
    MTLSSANEntry:
      type: object
      description: Subject Alternative Name entry
      properties:
        type:
          type: string
          enum:
            - DNS
            - URI
          description: SAN type
        value:
          type: string
          description: SAN value
    AddDomainMTLSCARequest:
      type: object
      required:
        - caPem
        - name
      description: Request to add CA certificate for domain mTLS
      properties:
        caPem:
          type: string
          description: CA certificate PEM content
        name:
          type: string
          description: Display name for the CA
    DomainYAMLs:
      type: object
      properties:
        gatewayYaml:
          type: string
        clientTrafficPolicyYaml:
          type: string
        backendTrafficPolicyYaml:
          type: string
        envoyExtensionPolicyYaml:
          type: string
    DomainSettingsPreviewInput:
      type: object
      properties:
        clientConnection:
          $ref: '#/components/schemas/ClientConnectionConfig'
        timeout:
          $ref: '#/components/schemas/DomainTimeoutConfig'
        http3:
          $ref: '#/components/schemas/HTTP3Config'
        tls:
          $ref: '#/components/schemas/TLSSettingsConfig'
        clientIPDetection:
          $ref: '#/components/schemas/ClientIPDetectionConfig'
        mtls:
          $ref: '#/components/schemas/DomainMTLSConfig'
        backendTrafficPolicy:
          $ref: '#/components/schemas/BackendTrafficPolicyConfig'
        extensionPolicy:
          $ref: '#/components/schemas/EnvoyExtensionPolicyConfig'
        description:
          type: string
        includeAIReview:
          type: boolean
          description: When true, triggers AI review of the proposed changes
    DomainSettingsPreviewResult:
      type: object
      properties:
        currentGatewayYaml:
          type: string
        currentClientTrafficPolicyYaml:
          type: string
        proposedClientTrafficPolicyYaml:
          type: string
        currentBackendTrafficPolicyYaml:
          type: string
        proposedBackendTrafficPolicyYaml:
          type: string
        currentEnvoyExtensionPolicyYaml:
          type: string
        proposedEnvoyExtensionPolicyYaml:
          type: string
        aiReview:
          $ref: '#/components/schemas/AIReviewResult'
    DomainCreatePreviewInput:
      type: object
      required:
        - name
        - hostname
        - domainTemplateId
      properties:
        name:
          type: string
        hostname:
          type: string
        domainTemplateId:
          type: string
          format: uuid
        tlsSecretName:
          type: string
        tlsSecretNamespace:
          type: string
          description: >-
            Kubernetes namespace containing the TLS secret. Must be managed by
            the project unless it is the default FastGateway namespace.
        namespace:
          type: string
          description: >-
            Kubernetes namespace to deploy the domain's gateway resources into.
            Defaults to the default FastGateway namespace; any other value must
            be registered for this project.
        labels:
          $ref: '#/components/schemas/Labels'
        description:
          type: string
        includeAIReview:
          type: boolean
          description: When true, triggers AI review of the proposed domain creation
    DomainCreatePreviewResult:
      type: object
      properties:
        proposedGatewayYaml:
          type: string
        aiReview:
          $ref: '#/components/schemas/AIReviewResult'
    TLSSecretInfo:
      type: object
      description: >-
        A kubernetes.io/tls Secret discovered in the cluster, available for use
        as a domain's TLS secret
      properties:
        name:
          type: string
        namespace:
          type: string
        managedByFastgateway:
          type: boolean
          description: >-
            True when the secret carries the
            app.kubernetes.io/managed-by=fastgateway label
        labels:
          $ref: '#/components/schemas/Labels'
        createdAt:
          type: string
          description: Secret creation timestamp (RFC3339), empty if unknown
    ListTLSSecretsResponse:
      type: object
      description: >-
        TLS secrets available in a namespace, plus the namespaces available to
        look up
      properties:
        namespace:
          type: string
          description: Namespace the secrets were listed from
        secrets:
          type: array
          items:
            $ref: '#/components/schemas/TLSSecretInfo'
        availableNamespaces:
          type: array
          items:
            type: string
          description: Namespaces eligible for domain/TLS secret deployment in this project
    AvailableNamespacesResponse:
      type: object
      properties:
        namespaces:
          type: array
          items:
            type: string
          description: Namespaces eligible for domain deployment in this project
    Route:
      type: object
      properties:
        id:
          type: string
          format: uuid
        domainId:
          type: string
          format: uuid
        teamId:
          type: string
          format: uuid
        team:
          $ref: '#/components/schemas/Team'
        name:
          type: string
        description:
          type: string
        protocol:
          type: string
          enum:
            - http
            - grpc
        status:
          type: string
          enum:
            - pending_create
            - pending_update
            - pending_delete
            - approved
            - pending_deploy
            - active
            - rejected
        config:
          $ref: '#/components/schemas/RouteConfig'
        securityMode:
          $ref: '#/components/schemas/SecurityMode'
        pendingApproval:
          $ref: '#/components/schemas/Approval'
        clientCount:
          type: integer
          description: Number of clients attached to this route
        securityStatus:
          type: string
          description: Security status summary
        createdBy:
          type: string
          format: uuid
        creator:
          $ref: '#/components/schemas/User'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        labels:
          $ref: '#/components/schemas/Labels'
    RouteList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Route'
        pagination:
          $ref: '#/components/schemas/Pagination'
    RouteConfig:
      type: object
      properties:
        routeType:
          type: string
          enum:
            - backend
            - redirect
            - directResponse
          default: backend
        matches:
          type: array
          items:
            $ref: '#/components/schemas/RouteMatch'
        backends:
          type: array
          items:
            $ref: '#/components/schemas/RouteBackend'
        mirrors:
          type: array
          items:
            $ref: '#/components/schemas/MirrorBackend'
          description: Mirror destinations for request mirroring (traffic shadowing)
        redirect:
          $ref: '#/components/schemas/RedirectConfig'
        directResponse:
          $ref: '#/components/schemas/DirectResponseConfig'
        requestHeaderModifier:
          $ref: '#/components/schemas/HeaderModifier'
        responseHeaderModifier:
          $ref: '#/components/schemas/HeaderModifier'
        urlRewrite:
          $ref: '#/components/schemas/URLRewrite'
        defaultTrafficPolicy:
          type: string
          enum:
            - allow_all
            - deny
            - require_ip_allowlist
          description: How non-client traffic is handled when clients are attached
        defaultAllowedCIDRs:
          type: array
          items:
            type: string
          description: CIDRs allowed when defaultTrafficPolicy is require_ip_allowlist
    GRPCMethodMatch:
      type: object
      properties:
        type:
          type: string
          enum:
            - Exact
            - RegularExpression
          default: Exact
          description: How to match the service or method name
        value:
          type: string
          description: gRPC service or method name to match
    RouteMatch:
      type: object
      properties:
        path:
          type: object
          properties:
            type:
              type: string
              enum:
                - Exact
                - Prefix
                - RegularExpression
            value:
              type: string
        headers:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              type:
                type: string
                enum:
                  - Exact
                  - RegularExpression
              value:
                type: string
        method:
          type: string
          enum:
            - GET
            - POST
            - PUT
            - DELETE
            - PATCH
            - HEAD
            - OPTIONS
        queryParams:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              type:
                type: string
                enum:
                  - Exact
                  - RegularExpression
              value:
                type: string
        grpcService:
          $ref: '#/components/schemas/GRPCMethodMatch'
          description: gRPC service name matcher (only for protocol=grpc)
        grpcMethod:
          $ref: '#/components/schemas/GRPCMethodMatch'
          description: gRPC method name matcher (only for protocol=grpc)
    RouteBackend:
      type: object
      required:
        - port
      properties:
        type:
          type: string
          enum:
            - kubernetes
            - external
          default: kubernetes
        service:
          type: string
          description: Service name (for kubernetes type)
        namespace:
          type: string
          description: Namespace (for kubernetes type)
        port:
          type: integer
        weight:
          type: integer
          minimum: 0
          maximum: 100
          default: 100
          description: Weight for traffic splitting (ignored for fallback backends)
        fallback:
          type: boolean
          default: false
          description: >-
            If true, this backend only receives traffic when primary backends
            are unhealthy
        addressType:
          type: string
          enum:
            - fqdn
            - ip
          description: Address type (for external type)
        address:
          type: string
          description: FQDN or IP address (for external type)
        tls:
          $ref: '#/components/schemas/BackendTLSConfig'
          description: TLS configuration (only valid for external backends)
    MirrorBackend:
      type: object
      required:
        - service
        - port
      description: Mirror destination for request mirroring (traffic shadowing)
      properties:
        type:
          type: string
          enum:
            - kubernetes
          default: kubernetes
          description: Backend type (currently only kubernetes is supported)
        service:
          type: string
          description: Service name in the target namespace
        namespace:
          type: string
          description: Namespace of the target service
        port:
          type: integer
          description: Port number of the target service
    BackendTLSConfig:
      type: object
      required:
        - caCertificateRefs
      description: >-
        TLS configuration for external backends. Simple TLS requires only
        caCertificateRefs. mTLS additionally requires clientCertificateRef.
      properties:
        clientCertificateRef:
          $ref: '#/components/schemas/SecretRef'
          description: Client certificate for mTLS (optional - if set, enables mTLS)
        caCertificateRefs:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/CertificateRef'
          description: CA certificates for verifying backend (required for TLS)
    SecretRef:
      type: object
      required:
        - name
      description: Reference to a Kubernetes Secret
      properties:
        name:
          type: string
          description: Name of the Secret
        namespace:
          type: string
          description: Namespace of the Secret (defaults to fastgateway-system)
    CertificateRef:
      type: object
      required:
        - kind
        - name
      description: Reference to a Kubernetes Secret or ConfigMap containing certificates
      properties:
        kind:
          type: string
          enum:
            - Secret
            - ConfigMap
          description: Kind of the resource
        name:
          type: string
          description: Name of the resource
        namespace:
          type: string
          description: Namespace of the resource (defaults to fastgateway-system)
    RedirectConfig:
      type: object
      properties:
        scheme:
          type: string
          enum:
            - http
            - https
        hostname:
          type: string
        port:
          type: integer
        statusCode:
          type: integer
          enum:
            - 301
            - 302
          default: 302
        path:
          $ref: '#/components/schemas/PathRewrite'
    DirectResponseConfig:
      type: object
      required:
        - statusCode
      description: >-
        Direct response configuration - return a static response without
        forwarding to backend
      properties:
        statusCode:
          type: integer
          minimum: 100
          maximum: 599
          description: HTTP status code to return
        contentType:
          type: string
          description: Content-Type header value (e.g., "text/plain", "application/json")
        body:
          $ref: '#/components/schemas/DirectResponseBody'
    DirectResponseBody:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - Inline
            - ValueRef
          description: >-
            Body type - Inline for direct content, ValueRef for ConfigMap
            reference
        inline:
          type: string
          maxLength: 4096
          description: Inline body content (max 4096 bytes)
    HeaderModifier:
      type: object
      properties:
        set:
          type: array
          items:
            $ref: '#/components/schemas/HeaderValue'
        add:
          type: array
          items:
            $ref: '#/components/schemas/HeaderValue'
        remove:
          type: array
          items:
            type: string
    HeaderValue:
      type: object
      required:
        - name
        - value
      properties:
        name:
          type: string
        value:
          type: string
    URLRewrite:
      type: object
      properties:
        hostname:
          type: string
        path:
          $ref: '#/components/schemas/PathRewrite'
    PathRewrite:
      type: object
      properties:
        type:
          type: string
          enum:
            - ReplacePrefixMatch
            - ReplaceFullPath
        replacePrefixMatch:
          type: string
        replaceFullPath:
          type: string
    CreateRouteRequest:
      type: object
      required:
        - name
        - teamId
        - config
      properties:
        name:
          type: string
        description:
          type: string
        protocol:
          type: string
          enum:
            - http
            - grpc
          default: http
        teamId:
          type: string
          format: uuid
        config:
          $ref: '#/components/schemas/RouteConfig'
        securityMode:
          $ref: '#/components/schemas/SecurityMode'
        securityPolicy:
          $ref: '#/components/schemas/SecurityPolicyInput'
        backendTrafficPolicy:
          $ref: '#/components/schemas/BackendTrafficPolicyInput'
        extensionPolicy:
          $ref: '#/components/schemas/EnvoyExtensionPolicyInput'
        wafPolicy:
          $ref: '#/components/schemas/WafPolicyConfig'
        changeDescription:
          type: string
          description: Optional description of the changes for approval
        aiReview:
          $ref: '#/components/schemas/AIReviewResult'
        labels:
          $ref: '#/components/schemas/Labels'
    UpdateRouteRequest:
      type: object
      properties:
        description:
          type: string
        config:
          $ref: '#/components/schemas/RouteConfig'
        securityPolicy:
          $ref: '#/components/schemas/SecurityPolicyInput'
        backendTrafficPolicy:
          $ref: '#/components/schemas/BackendTrafficPolicyInput'
        extensionPolicy:
          $ref: '#/components/schemas/EnvoyExtensionPolicyInput'
        wafPolicy:
          $ref: '#/components/schemas/WafPolicyConfig'
        changeDescription:
          type: string
          description: Optional description of the changes for approval
        aiReview:
          $ref: '#/components/schemas/AIReviewResult'
        labels:
          $ref: '#/components/schemas/Labels'
    SecurityPolicyInput:
      type: object
      properties:
        cors:
          $ref: '#/components/schemas/CORSConfig'
        authorization:
          type: object
          description: General mode IP/header/method allowlisting
          properties:
            allowedCIDRs:
              type: array
              items:
                type: string
            headers:
              type: array
              items:
                $ref: '#/components/schemas/AuthorizationHeaderMatch'
              description: Required header match rules
            methods:
              type: array
              items:
                type: string
              description: Allowed HTTP methods
        apiKeyAuth:
          type: object
          properties:
            secretName:
              type: string
            headerName:
              type: string
        jwt:
          type: object
          properties:
            issuer:
              type: string
            jwksUrl:
              type: string
            audiences:
              type: array
              items:
                type: string
            claimToHeaders:
              type: array
              items:
                type: object
                properties:
                  claim:
                    type: string
                  header:
                    type: string
        oidc:
          type: object
          properties:
            issuer:
              type: string
            clientId:
              type: string
            clientSecretName:
              type: string
            redirectURL:
              type: string
            logoutPath:
              type: string
            scopes:
              type: array
              items:
                type: string
            cookieDomain:
              type: string
        extAuth:
          $ref: '#/components/schemas/ExtAuthConfig'
    SecurityMode:
      type: string
      enum:
        - general
        - client
      description: >-
        Security configuration mode. 'general' configures security directly on
        the route. 'client' uses client attachments.
    RouteYAMLsResponse:
      type: object
      required:
        - httpRouteYaml
      properties:
        httpRouteYaml:
          type: string
          description: HTTPRoute Kubernetes YAML
        securityPolicyYaml:
          type: string
          description: SecurityPolicy Kubernetes YAML (if configured)
        backendTrafficPolicyYaml:
          type: string
          description: BackendTrafficPolicy Kubernetes YAML (if configured)
        envoyExtensionPolicyYaml:
          type: string
          description: EnvoyExtensionPolicy Kubernetes YAML (if configured)
        backendYaml:
          type: string
          description: >-
            Backend CRD Kubernetes YAML for external service backends (if
            configured)
        httpRouteFilterYaml:
          type: string
          description: >-
            HTTPRouteFilter Kubernetes YAML for direct-response routes (if
            configured)
        configMapYaml:
          type: string
          description: >-
            ConfigMap Kubernetes YAML backing a direct-response body (if
            configured)
        apiKeyClientResources:
          type: array
          items:
            $ref: '#/components/schemas/APIKeyClientResourceYAMLs'
          description: >-
            Per-client Kubernetes resources for clients using API-key auth
            (secrets redacted)
    PreviewCreateResponse:
      type: object
      required:
        - proposedYaml
      properties:
        proposedYaml:
          type: string
          description: Proposed HTTPRoute YAML
        proposedSecurityPolicyYaml:
          type: string
          description: Proposed SecurityPolicy YAML (if configured)
        proposedBackendTrafficPolicyYaml:
          type: string
          description: Proposed BackendTrafficPolicy YAML (if configured)
        proposedBackendYaml:
          type: string
          description: >-
            Proposed Backend CRD YAML for external service backends (if
            configured)
        proposedEnvoyExtensionPolicyYaml:
          type: string
          description: >-
            Proposed EnvoyExtensionPolicy YAML for Lua/Wasm extensions (if
            configured)
        proposedHttpRouteFilterYaml:
          type: string
          description: >-
            Proposed HTTPRouteFilter YAML for direct-response routes (if
            configured)
        proposedConfigMapYaml:
          type: string
          description: >-
            Proposed ConfigMap YAML backing a direct-response body (if
            configured)
    PreviewUpdateResponse:
      type: object
      required:
        - currentYaml
        - proposedYaml
      properties:
        currentYaml:
          type: string
          description: Current HTTPRoute YAML
        proposedYaml:
          type: string
          description: Proposed HTTPRoute YAML
        currentSecurityPolicyYaml:
          type: string
          description: Current SecurityPolicy YAML (if exists)
        proposedSecurityPolicyYaml:
          type: string
          description: Proposed SecurityPolicy YAML (if configured)
        currentBackendTrafficPolicyYaml:
          type: string
          description: Current BackendTrafficPolicy YAML (if exists)
        proposedBackendTrafficPolicyYaml:
          type: string
          description: Proposed BackendTrafficPolicy YAML (if configured)
        currentBackendYaml:
          type: string
          description: Current Backend CRD YAML for external service backends (if exists)
        proposedBackendYaml:
          type: string
          description: >-
            Proposed Backend CRD YAML for external service backends (if
            configured)
        currentEnvoyExtensionPolicyYaml:
          type: string
          description: Current EnvoyExtensionPolicy YAML (if exists)
        proposedEnvoyExtensionPolicyYaml:
          type: string
          description: >-
            Proposed EnvoyExtensionPolicy YAML for Lua/Wasm extensions (if
            configured)
        currentHttpRouteFilterYaml:
          type: string
          description: Current HTTPRouteFilter YAML for direct-response routes (if exists)
        proposedHttpRouteFilterYaml:
          type: string
          description: >-
            Proposed HTTPRouteFilter YAML for direct-response routes (if
            configured)
        currentConfigMapYaml:
          type: string
          description: Current ConfigMap YAML backing a direct-response body (if exists)
        proposedConfigMapYaml:
          type: string
          description: >-
            Proposed ConfigMap YAML backing a direct-response body (if
            configured)
    PreviewDeleteResponse:
      type: object
      required:
        - currentYaml
      properties:
        currentYaml:
          type: string
          description: Current HTTPRoute YAML (will be deleted)
        currentSecurityPolicyYaml:
          type: string
          description: Current SecurityPolicy YAML (will be deleted if exists)
        currentBackendTrafficPolicyYaml:
          type: string
          description: Current BackendTrafficPolicy YAML (will be deleted if exists)
        currentBackendYaml:
          type: string
          description: >-
            Current Backend CRD YAML for external service backends (will be
            deleted if exists)
        currentEnvoyExtensionPolicyYaml:
          type: string
          description: Current EnvoyExtensionPolicy YAML (will be deleted if exists)
        currentHttpRouteFilterYaml:
          type: string
          description: >-
            Current HTTPRouteFilter YAML for direct-response routes (will be
            deleted if exists)
        currentConfigMapYaml:
          type: string
          description: >-
            Current ConfigMap YAML backing a direct-response body (will be
            deleted if exists)
    SecurityPolicy:
      type: object
      properties:
        id:
          type: string
          format: uuid
        routeId:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        config:
          $ref: '#/components/schemas/SecurityPolicyConfig'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    SecurityPolicyConfig:
      type: object
      properties:
        cors:
          $ref: '#/components/schemas/CORSConfig'
        authorization:
          $ref: '#/components/schemas/AuthorizationConfig'
        apiKeyAuth:
          $ref: '#/components/schemas/APIKeyAuthStoredConfig'
        basicAuth:
          $ref: '#/components/schemas/BasicAuthConfig'
        jwt:
          $ref: '#/components/schemas/JWTStoredConfig'
        oidc:
          $ref: '#/components/schemas/OIDCStoredConfig'
        extAuth:
          $ref: '#/components/schemas/ExtAuthConfig'
    IPAllowlistEntry:
      type: object
      required:
        - cidr
      properties:
        cidr:
          type: string
          description: IP address or CIDR range (e.g., 10.0.0.1/32)
        description:
          type: string
          description: Optional description for this IP entry
    AuthorizationConfig:
      type: object
      description: >-
        Authorization configuration computed at deploy time from active client
        attachments
      properties:
        defaultAction:
          type: string
          enum:
            - Deny
          description: Default action when IP allowlisting is enabled
        rules:
          type: array
          items:
            $ref: '#/components/schemas/AuthorizationRule'
    AuthorizationRule:
      type: object
      properties:
        action:
          type: string
          enum:
            - Allow
        principal:
          $ref: '#/components/schemas/AuthorizationPrincipal'
    AuthorizationPrincipal:
      type: object
      properties:
        clientCIDRs:
          type: array
          items:
            type: string
          description: List of client CIDRs allowed
        jwt:
          $ref: '#/components/schemas/JWTPrincipal'
    CORSConfig:
      type: object
      properties:
        allowOrigins:
          type: array
          items:
            type: string
          description: Origins allowed (supports wildcards)
        allowMethods:
          type: array
          items:
            type: string
          description: HTTP methods allowed
        allowHeaders:
          type: array
          items:
            type: string
          description: Headers allowed in requests
        exposeHeaders:
          type: array
          items:
            type: string
          description: Headers exposed to the browser
        maxAge:
          type: integer
          description: Max age in seconds for preflight cache
        allowCredentials:
          type: boolean
          description: Whether to allow credentials
    APIKeyAuthStoredConfig:
      type: object
      description: Stored API key auth configuration (K8s-native format)
      properties:
        credentialRefs:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
        extractFrom:
          type: object
          properties:
            headers:
              type: array
              items:
                type: string
    BasicAuthConfig:
      type: object
      description: Basic auth configuration
      properties:
        secretRef:
          type: object
          properties:
            name:
              type: string
            namespace:
              type: string
    JWTStoredConfig:
      type: object
      description: Stored JWT configuration (K8s-native format)
      properties:
        providers:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              issuer:
                type: string
              audiences:
                type: array
                items:
                  type: string
              remoteJWKS:
                type: object
                properties:
                  uri:
                    type: string
              claimToHeaders:
                type: array
                items:
                  type: object
                  properties:
                    claim:
                      type: string
                    header:
                      type: string
    OIDCStoredConfig:
      type: object
      description: Stored OIDC configuration
      properties:
        provider:
          type: object
          properties:
            issuer:
              type: string
            authorizationURL:
              type: string
            tokenURL:
              type: string
        clientId:
          type: string
        clientSecret:
          type: string
        redirectURL:
          type: string
        logoutPath:
          type: string
        scopes:
          type: array
          items:
            type: string
        cookieDomain:
          type: string
    OIDCConfig:
      type: object
      properties:
        issuer:
          type: string
        clientId:
          type: string
        clientSecretName:
          type: string
        redirectURL:
          type: string
        logoutPath:
          type: string
        scopes:
          type: array
          items:
            type: string
        cookieDomain:
          type: string
    JWTPrincipal:
      type: object
      description: JWT principal for authorization rules
      properties:
        provider:
          type: string
          description: JWT provider name
        claims:
          type: array
          items:
            $ref: '#/components/schemas/JWTClaimRule'
    JWTClaimRule:
      type: object
      description: JWT claim matching rule
      properties:
        name:
          type: string
          description: Claim name
        values:
          type: array
          items:
            type: string
          description: Required claim values
        valueType:
          type: string
          enum:
            - Exact
            - StringContains
          description: How to match claim values
    ExtAuthConfig:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - http
            - grpc
          description: External auth service type
        http:
          $ref: '#/components/schemas/ExtAuthHTTPConfig'
        grpc:
          $ref: '#/components/schemas/ExtAuthGRPCConfig'
        failOpen:
          type: boolean
          description: Allow traffic if auth service is unavailable
        headersToExtAuth:
          type: array
          items:
            type: string
          description: Additional client request headers to forward to ext-auth service
        headersToDownstreamOnDeny:
          type: array
          items:
            type: string
          description: Headers to send to client on auth deny
        headersToDownstreamOnAllow:
          type: array
          items:
            type: string
          description: Headers to send to client on auth allow
        headersToUpstreamOnAllow:
          type: array
          items:
            type: string
          description: Headers to send to backend on auth allow
        withRequestBody:
          $ref: '#/components/schemas/ExtAuthRequestBody'
    ExtAuthHTTPConfig:
      type: object
      required:
        - backendRef
        - path
      properties:
        backendRef:
          $ref: '#/components/schemas/ExtAuthBackendRef'
        path:
          type: string
          description: Path on the auth service to call
        headersToBackend:
          type: array
          items:
            type: string
          description: Headers to forward to auth service
    ExtAuthGRPCConfig:
      type: object
      required:
        - backendRef
      properties:
        backendRef:
          $ref: '#/components/schemas/ExtAuthBackendRef'
    ExtAuthBackendRef:
      type: object
      required:
        - name
        - port
      properties:
        name:
          type: string
          description: Service name
        namespace:
          type: string
          description: Namespace (optional, defaults to route namespace)
        port:
          type: integer
          description: Service port
    ExtAuthRequestBody:
      type: object
      required:
        - maxBytes
      properties:
        maxBytes:
          type: integer
          description: Maximum request body bytes to forward
    BackendTrafficPolicy:
      type: object
      properties:
        id:
          type: string
          format: uuid
        routeId:
          type: string
          format: uuid
        domainId:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        config:
          $ref: '#/components/schemas/BackendTrafficPolicyConfig'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    BackendTrafficPolicyConfig:
      type: object
      properties:
        compression:
          type: array
          items:
            $ref: '#/components/schemas/CompressionConfig'
          description: List of compression configurations
        retry:
          $ref: '#/components/schemas/RetryConfig'
        loadBalancer:
          $ref: '#/components/schemas/LoadBalancerConfig'
        circuitBreaker:
          $ref: '#/components/schemas/CircuitBreakerConfig'
        healthCheck:
          $ref: '#/components/schemas/HealthCheckConfig'
        faultInjection:
          $ref: '#/components/schemas/FaultInjectionConfig'
        rateLimit:
          $ref: '#/components/schemas/RateLimitConfig'
        requestBuffer:
          $ref: '#/components/schemas/RequestBufferConfig'
        responseOverride:
          type: array
          items:
            $ref: '#/components/schemas/ResponseOverrideRule'
          description: Rules for overriding backend responses based on status codes
        timeout:
          $ref: '#/components/schemas/BTPTimeoutConfig'
    BackendTrafficPolicyInput:
      type: object
      properties:
        compression:
          type: array
          items:
            $ref: '#/components/schemas/CompressionConfig'
          description: List of compression configurations
        retry:
          $ref: '#/components/schemas/RetryConfig'
        loadBalancer:
          $ref: '#/components/schemas/LoadBalancerConfig'
        circuitBreaker:
          $ref: '#/components/schemas/CircuitBreakerConfig'
        healthCheck:
          $ref: '#/components/schemas/HealthCheckConfig'
        faultInjection:
          $ref: '#/components/schemas/FaultInjectionConfig'
        rateLimit:
          $ref: '#/components/schemas/RateLimitConfig'
        requestBuffer:
          $ref: '#/components/schemas/RequestBufferConfig'
        responseOverride:
          type: array
          items:
            $ref: '#/components/schemas/ResponseOverrideRule'
          description: Rules for overriding backend responses based on status codes
        timeout:
          $ref: '#/components/schemas/BTPTimeoutConfig'
    BTPTimeoutConfig:
      type: object
      description: >-
        Timeout configuration for BackendTrafficPolicy (works for both HTTP and
        gRPC routes)
      properties:
        tcp:
          $ref: '#/components/schemas/BTPTCPTimeoutConfig'
        http:
          $ref: '#/components/schemas/BTPHTTPTimeoutConfig'
    BTPTCPTimeoutConfig:
      type: object
      properties:
        connectTimeout:
          type: string
          description: TCP connect timeout (e.g. "10s", "500ms")
    BTPHTTPTimeoutConfig:
      type: object
      properties:
        requestTimeout:
          type: string
          description: Request timeout (e.g. "15s", "1m")
        connectionIdleTimeout:
          type: string
          description: Connection idle timeout (e.g. "1h")
        maxConnectionDuration:
          type: string
          description: Max connection duration (e.g. "0s" for unlimited)
        maxStreamDuration:
          type: string
          description: Max stream duration (e.g. "0s" for unlimited)
    CompressionConfig:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - Gzip
            - Brotli
            - Zstd
          description: Compression algorithm type
        gzip:
          type: object
          description: Gzip-specific configuration (currently empty)
        brotli:
          type: object
          description: Brotli-specific configuration (currently empty)
        zstd:
          type: object
          description: Zstd-specific configuration (currently empty)
    RetryConfig:
      type: object
      description: Retry configuration for BackendTrafficPolicy
      properties:
        numRetries:
          type: integer
          minimum: 0
          description: Maximum number of retry attempts. Defaults to 2 if not set.
        retryOn:
          $ref: '#/components/schemas/RetryOn'
        perRetryPolicy:
          $ref: '#/components/schemas/PerRetryPolicy'
    RetryOn:
      type: object
      description: Conditions that trigger a retry
      properties:
        httpStatusCodes:
          type: array
          items:
            type: integer
            minimum: 100
            maximum: 599
          description: HTTP status codes that trigger a retry
        triggers:
          type: array
          items:
            type: string
            enum:
              - 5xx
              - gateway-error
              - connect-failure
              - retriable-status-codes
              - reset
              - reset-before-request
              - retriable-4xx
              - refused-stream
              - cancelled
              - deadline-exceeded
              - internal
              - resource-exhausted
              - unavailable
          description: Envoy retry trigger conditions
    PerRetryPolicy:
      type: object
      description: Per-attempt retry behavior
      properties:
        backOff:
          $ref: '#/components/schemas/BackOffPolicy'
        timeout:
          type: string
          description: Per-attempt timeout (Go duration format, e.g. 250ms, 1s)
    BackOffPolicy:
      type: object
      description: Backoff timing between retries
      properties:
        baseInterval:
          type: string
          description: Initial wait between retries (e.g. 25ms, 100ms)
        maxInterval:
          type: string
          description: >-
            Maximum wait between retries (e.g. 250ms, 10s). Must be >=
            baseInterval.
    LoadBalancerConfig:
      type: object
      required:
        - type
      description: Load balancer configuration for traffic distribution across backends
      properties:
        type:
          type: string
          enum:
            - RoundRobin
            - Random
            - LeastRequest
            - ConsistentHash
          description: Load balancing algorithm type
        consistentHash:
          $ref: '#/components/schemas/ConsistentHashConfig'
    ConsistentHashConfig:
      type: object
      required:
        - type
      description: >-
        Consistent hash configuration (required when load balancer type is
        ConsistentHash)
      properties:
        type:
          type: string
          enum:
            - SourceIP
            - Header
            - Cookie
          description: The hash key type for consistent hashing
        header:
          $ref: '#/components/schemas/ConsistentHashHeader'
        cookie:
          $ref: '#/components/schemas/ConsistentHashCookie'
    ConsistentHashHeader:
      type: object
      required:
        - name
      description: Header-based consistent hashing configuration
      properties:
        name:
          type: string
          description: The HTTP header name to hash on
    ConsistentHashCookie:
      type: object
      required:
        - name
      description: Cookie-based consistent hashing configuration
      properties:
        name:
          type: string
          description: The cookie name to hash on
        ttl:
          type: string
          description: Cookie TTL duration (Go duration format, e.g. "60s", "1h")
        attributes:
          type: object
          additionalProperties:
            type: string
          description: Cookie attributes (e.g. SameSite=Strict, HttpOnly=true)
    CircuitBreakerConfig:
      type: object
      description: Circuit breaker configuration to protect backends from being overwhelmed
      properties:
        maxConnections:
          type: integer
          format: int64
          description: Maximum number of connections to the backend. Default 1024.
        maxPendingRequests:
          type: integer
          format: int64
          description: Maximum number of pending requests queued. Default 1024.
        maxParallelRequests:
          type: integer
          format: int64
          description: Maximum number of parallel requests. Default 1024.
        maxParallelRetries:
          type: integer
          format: int64
          description: Maximum number of parallel retries. Default 1024.
        maxRequestsPerConnection:
          type: integer
          format: int64
          description: >-
            Maximum requests per connection. Default unlimited. Set to 1 to
            disable keep-alive.
    HealthCheckConfig:
      type: object
      description: Health check configuration for backend services
      properties:
        active:
          $ref: '#/components/schemas/ActiveHealthCheckConfig'
        passive:
          $ref: '#/components/schemas/PassiveHealthCheckConfig'
        panicThreshold:
          type: integer
          format: uint32
          description: Panic threshold percentage. Below this, Envoy ignores health status.
    ActiveHealthCheckConfig:
      type: object
      required:
        - type
      description: Active health check - Envoy actively probes backends
      properties:
        timeout:
          type: string
          description: Time to wait for response. Default 1s.
        interval:
          type: string
          description: Time between checks. Default 3s.
        unhealthyThreshold:
          type: integer
          format: uint32
          description: Unhealthy checks to mark backend unhealthy. Default 3.
        healthyThreshold:
          type: integer
          format: uint32
          description: Healthy checks to mark backend healthy. Default 1.
        type:
          type: string
          enum:
            - HTTP
            - TCP
            - GRPC
        http:
          $ref: '#/components/schemas/HTTPActiveHealthCheckConfig'
        tcp:
          $ref: '#/components/schemas/TCPActiveHealthCheckConfig'
        grpc:
          $ref: '#/components/schemas/GRPCActiveHealthCheckConfig'
    HTTPActiveHealthCheckConfig:
      type: object
      required:
        - path
      properties:
        path:
          type: string
          description: HTTP request path for health checks
        method:
          type: string
          enum:
            - GET
            - HEAD
            - POST
            - PUT
            - DELETE
            - OPTIONS
            - PATCH
          description: HTTP method. Default GET.
        expectedStatuses:
          type: array
          items:
            type: integer
          description: Expected HTTP status codes. Default [200].
    TCPActiveHealthCheckConfig:
      type: object
      properties:
        send:
          $ref: '#/components/schemas/HealthCheckPayload'
        receive:
          $ref: '#/components/schemas/HealthCheckPayload'
    GRPCActiveHealthCheckConfig:
      type: object
      properties:
        service:
          type: string
          description: gRPC service name for health checks
    HealthCheckPayload:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - Text
        text:
          type: string
    PassiveHealthCheckConfig:
      type: object
      description: Passive health check (outlier detection)
      properties:
        consecutiveGatewayErrors:
          type: integer
          format: uint32
          description: Gateway error threshold to eject backend
        consecutive5xxErrors:
          type: integer
          format: uint32
          description: 5xx error threshold to eject backend
        interval:
          type: string
          description: Evaluation interval (e.g. "30s")
        baseEjectionTime:
          type: string
          description: Minimum ejection duration (e.g. "30s")
    FaultInjectionConfig:
      type: object
      description: Fault injection configuration for testing resilience
      properties:
        delay:
          $ref: '#/components/schemas/FaultInjectionDelayConfig'
        abort:
          $ref: '#/components/schemas/FaultInjectionAbortConfig'
    FaultInjectionDelayConfig:
      type: object
      required:
        - fixedDelay
      description: Delay fault injection - adds artificial latency
      properties:
        fixedDelay:
          type: string
          description: Duration to delay (e.g. "2s", "500ms")
        percentage:
          type: number
          format: float
          minimum: 0
          maximum: 100
          description: Percentage of requests to delay (0-100). Default 100.
    FaultInjectionAbortConfig:
      type: object
      description: Abort fault injection - returns error responses
      properties:
        httpStatus:
          type: integer
          minimum: 100
          maximum: 599
          description: HTTP status code to return
        grpcStatus:
          type: integer
          minimum: 0
          maximum: 16
          description: gRPC status code to return
        percentage:
          type: number
          format: float
          minimum: 0
          maximum: 100
          description: Percentage of requests to abort (0-100). Default 100.
    RateLimitConfig:
      type: object
      description: Rate limit configuration for controlling request volume
      properties:
        global:
          $ref: '#/components/schemas/GlobalRateLimitConfig'
    GlobalRateLimitConfig:
      type: object
      required:
        - rules
      description: Global rate limit configuration
      properties:
        rules:
          type: array
          items:
            $ref: '#/components/schemas/RateLimitRule'
          minItems: 1
          description: List of rate limit rules
    RateLimitRule:
      type: object
      required:
        - limit
      description: A single rate limit rule
      properties:
        limit:
          $ref: '#/components/schemas/RateLimitValue'
        clientSelectors:
          type: array
          items:
            $ref: '#/components/schemas/RateLimitSelector'
          description: Optional client selectors to scope the rate limit
    RateLimitValue:
      type: object
      required:
        - requests
        - unit
      description: Rate limit value (requests per time unit)
      properties:
        requests:
          type: integer
          minimum: 1
          description: Number of requests allowed
        unit:
          type: string
          enum:
            - Second
            - Minute
            - Hour
            - Day
          description: Time unit for the rate limit
    RateLimitSelector:
      type: object
      description: Client selector criteria for rate limiting
      properties:
        headers:
          type: array
          items:
            $ref: '#/components/schemas/RateLimitHeaderMatch'
          description: Header-based matching
        sourceCIDR:
          $ref: '#/components/schemas/RateLimitSourceCIDR'
        path:
          $ref: '#/components/schemas/RateLimitPathMatch'
        methods:
          type: array
          items:
            type: string
          description: HTTP methods to match (e.g., GET, POST)
    RateLimitHeaderMatch:
      type: object
      required:
        - name
      description: Header match for rate limiting
      properties:
        name:
          type: string
          description: Header name to match
        value:
          type: string
          description: Header value to match (optional)
        type:
          type: string
          enum:
            - Exact
            - Distinct
          description: Match type (Exact = match value, Distinct = unique per value)
        invert:
          type: boolean
          default: false
          description: Invert the match condition
    RateLimitSourceCIDR:
      type: object
      required:
        - value
      description: Source CIDR match for rate limiting
      properties:
        value:
          type: string
          description: CIDR notation or IP address (e.g., "192.168.1.0/24")
        type:
          type: string
          enum:
            - Exact
            - Distinct
          description: Match type (Exact = match CIDR, Distinct = unique per IP)
    RateLimitPathMatch:
      type: object
      required:
        - value
        - type
      description: Path match for rate limiting
      properties:
        value:
          type: string
          description: Path value to match
        type:
          type: string
          enum:
            - Exact
            - PathPrefix
            - RegularExpression
          description: Path match type
    RequestBufferConfig:
      type: object
      required:
        - limit
      description: Request buffering configuration
      properties:
        limit:
          type: string
          description: Buffer limit (e.g., "4Ki", "1Mi", "16Ki")
    ResponseOverrideRule:
      type: object
      required:
        - match
        - response
      description: Response override rule
      properties:
        match:
          $ref: '#/components/schemas/ResponseOverrideMatch'
        response:
          $ref: '#/components/schemas/ResponseOverrideResponse'
    ResponseOverrideMatch:
      type: object
      required:
        - statusCodes
      description: Match criteria for response override
      properties:
        statusCodes:
          type: array
          items:
            $ref: '#/components/schemas/StatusCodeMatch'
          description: Status codes to match
    StatusCodeMatch:
      type: object
      required:
        - type
      description: Status code match specification
      properties:
        type:
          type: string
          enum:
            - Value
            - Range
          description: Match type (single value or range)
        value:
          type: integer
          description: Single status code value (when type is Value)
        range:
          $ref: '#/components/schemas/StatusCodeRange'
    StatusCodeRange:
      type: object
      required:
        - start
        - end
      description: Status code range
      properties:
        start:
          type: integer
          description: Start of range (inclusive)
        end:
          type: integer
          description: End of range (inclusive)
    ResponseOverrideResponse:
      type: object
      required:
        - contentType
        - body
      description: Override response configuration
      properties:
        contentType:
          type: string
          description: Content-Type header for the response
        body:
          $ref: '#/components/schemas/ResponseOverrideBody'
    ResponseOverrideBody:
      type: object
      required:
        - type
      description: Response body configuration
      properties:
        type:
          type: string
          enum:
            - Inline
            - ValueRef
          description: Body source type
        inline:
          type: string
          description: Inline body content (when type is Inline)
        valueRef:
          $ref: '#/components/schemas/ValueRef'
    ValueRef:
      type: object
      required:
        - kind
        - name
      description: Reference to a ConfigMap or Secret
      properties:
        group:
          type: string
          description: API group (empty for core resources)
        kind:
          type: string
          enum:
            - ConfigMap
            - Secret
          description: Resource kind
        name:
          type: string
          description: Resource name
        namespace:
          type: string
          description: Resource namespace (optional)
    Client:
      type: object
      properties:
        id:
          type: string
          format: uuid
        teamId:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        contactName:
          type: string
        contactEmail:
          type: string
        ipAddressCount:
          type: integer
        headerCount:
          type: integer
        attachmentCount:
          type: integer
        apiKeyEnabled:
          type: boolean
          description: Whether the client has an API key configured
        apiKeyPrefix:
          type: string
          description: First few characters of the API key (e.g., "fg_live_xxxx")
        apiKeyHeaderName:
          type: string
          description: Header name used to send the API key (default "x-api-key")
        apiKeyCreatedAt:
          type: string
          format: date-time
          description: When the API key was generated
        apiKeyCreatedBy:
          type: string
          format: uuid
          description: User who generated the API key
        clientIdHeaderName:
          type: string
          description: Header name for client ID routing (default "x-client-id")
        jwtEnabled:
          type: boolean
          description: Whether the client has JWT authentication configured
        jwtIssuer:
          type: string
          description: JWT issuer URL
        jwtJwksUrl:
          type: string
          description: URL to fetch JWKS for JWT validation
        jwtAudiences:
          type: array
          items:
            type: string
          description: Expected JWT audience values
        jwtRequiredClaims:
          type: array
          items:
            $ref: '#/components/schemas/JWTRequiredClaim'
          description: Required JWT claims for authorization
        jwtClaimToHeaders:
          type: array
          items:
            $ref: '#/components/schemas/JWTClaimToHeader'
          description: JWT claims to map to HTTP headers
        jwtCreatedAt:
          type: string
          format: date-time
          description: When JWT was configured
        jwtCreatedBy:
          type: string
          format: uuid
          description: User who configured JWT
        jwtCreator:
          $ref: '#/components/schemas/User'
        mtlsEnabled:
          type: boolean
          description: Whether the client has mTLS authentication configured
        mtlsCaName:
          type: string
          description: mTLS CA certificate name
        mtlsCaSecret:
          type: string
          description: K8s Secret name containing CA cert
        mtlsCaSecretKey:
          type: string
          description: Key within the Secret
        mtlsSans:
          type: array
          items:
            $ref: '#/components/schemas/MTLSSANEntry'
          description: Subject Alternative Names for mTLS
        mtlsHashes:
          type: array
          items:
            type: string
          description: Certificate hash whitelist for mTLS
        mtlsCreatedAt:
          type: string
          format: date-time
          description: When mTLS was configured
        mtlsCreatedBy:
          type: string
          format: uuid
          description: User who configured mTLS
        mtlsCreator:
          $ref: '#/components/schemas/User'
        managedCertificateId:
          type: string
          format: uuid
          description: >-
            Set when a managed client-usage certificate (see PUT
            .../certificate) is attached. Its CA Secret and ClientTrafficPolicy
            materialize at the next domain deploy.
        allowedMethods:
          type: array
          items:
            type: string
            enum:
              - GET
              - POST
              - PUT
              - DELETE
              - PATCH
              - HEAD
              - OPTIONS
          description: Allowed HTTP methods for authorization
        createdBy:
          type: string
          format: uuid
        team:
          $ref: '#/components/schemas/Team'
        creator:
          $ref: '#/components/schemas/User'
        apiKeyCreator:
          $ref: '#/components/schemas/User'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    ClientList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Client'
        pagination:
          $ref: '#/components/schemas/Pagination'
    CreateClientRequest:
      type: object
      required:
        - name
        - teamId
      properties:
        name:
          type: string
        description:
          type: string
        teamId:
          type: string
          format: uuid
        contactName:
          type: string
        contactEmail:
          type: string
        clientIdHeaderName:
          type: string
          description: Header name for client ID routing (default "x-client-id")
    UpdateClientRequest:
      type: object
      properties:
        name:
          type: string
        description:
          type: string
        contactName:
          type: string
        contactEmail:
          type: string
        clientIdHeaderName:
          type: string
          description: Header name for client ID routing (default "x-client-id")
    ClientIPAddress:
      type: object
      properties:
        id:
          type: string
          format: uuid
        clientId:
          type: string
          format: uuid
        cidr:
          type: string
          description: IP address or CIDR range (e.g. 10.0.0.1/32, 192.168.0.0/24)
        description:
          type: string
        createdBy:
          type: string
          format: uuid
        creator:
          $ref: '#/components/schemas/User'
        createdAt:
          type: string
          format: date-time
    CreateClientIPRequest:
      type: object
      required:
        - cidr
      properties:
        cidr:
          type: string
          description: IP address or CIDR range
        description:
          type: string
    GenerateAPIKeyRequest:
      type: object
      properties:
        headerName:
          type: string
          description: Header name to send the API key in requests (default "x-api-key")
          default: x-api-key
          example: x-api-key
    GenerateAPIKeyResponse:
      type: object
      properties:
        apiKey:
          type: string
          description: The generated API key (shown only once)
          example: fg_live_aB3cD4eF5gH6iJ7kL8mN9oP0qR1sT2u
        prefix:
          type: string
          description: First few characters of the key for display
          example: fg_live_aB3c
        headerName:
          type: string
          description: Header name to use when sending the key
          example: x-api-key
        createdAt:
          type: string
          format: date-time
          description: When the key was generated
    JWTRequiredClaim:
      type: object
      properties:
        name:
          type: string
          description: JWT claim name (e.g., "scope", "role")
        values:
          type: array
          items:
            type: string
          description: Required values for the claim
        valueType:
          type: string
          enum:
            - Exact
            - StringContains
          default: Exact
          description: How to match claim values
    JWTClaimToHeader:
      type: object
      properties:
        claim:
          type: string
          description: JWT claim name to extract
        header:
          type: string
          description: HTTP header name to set with the claim value
    ConfigureJWTRequest:
      type: object
      required:
        - issuer
        - jwksUrl
      properties:
        issuer:
          type: string
          description: JWT issuer URL
          example: https://accounts.google.com
        jwksUrl:
          type: string
          description: URL to fetch JWKS for JWT validation
          example: https://www.googleapis.com/oauth2/v3/certs
        audiences:
          type: array
          items:
            type: string
          description: Expected JWT audience values
        requiredClaims:
          type: array
          items:
            $ref: '#/components/schemas/JWTRequiredClaim'
          description: Required JWT claims for authorization
        claimToHeaders:
          type: array
          items:
            $ref: '#/components/schemas/JWTClaimToHeader'
          description: JWT claims to map to HTTP headers
    ConfigureJWTResponse:
      type: object
      properties:
        jwtEnabled:
          type: boolean
        jwtIssuer:
          type: string
        jwtJwksUrl:
          type: string
        jwtAudiences:
          type: array
          items:
            type: string
        jwtRequiredClaims:
          type: array
          items:
            $ref: '#/components/schemas/JWTRequiredClaim'
        jwtClaimToHeaders:
          type: array
          items:
            $ref: '#/components/schemas/JWTClaimToHeader'
        jwtCreatedAt:
          type: string
          format: date-time
        jwtCreatedBy:
          type: string
          format: uuid
    ClientHeader:
      type: object
      properties:
        id:
          type: string
          format: uuid
        clientId:
          type: string
          format: uuid
        name:
          type: string
          description: Header name (e.g., "x-user-id")
        values:
          type: array
          items:
            type: string
          description: Allowed values for this header
        description:
          type: string
        createdBy:
          type: string
          format: uuid
        creator:
          $ref: '#/components/schemas/User'
        createdAt:
          type: string
          format: date-time
    CreateClientHeaderRequest:
      type: object
      required:
        - name
        - values
      properties:
        name:
          type: string
          description: Header name (e.g., "x-user-id")
        values:
          type: array
          items:
            type: string
          description: Allowed values for this header
        description:
          type: string
    SetAllowedMethodsRequest:
      type: object
      required:
        - methods
      properties:
        methods:
          type: array
          items:
            type: string
            enum:
              - GET
              - POST
              - PUT
              - DELETE
              - PATCH
              - HEAD
              - OPTIONS
          description: Allowed HTTP methods for this client
    UpdateClientMTLSRequest:
      type: object
      description: Request to configure mTLS for a client
      properties:
        enabled:
          type: boolean
        caName:
          type: string
          description: CA certificate name
        caPem:
          type: string
          description: CA certificate PEM content
        sans:
          type: array
          items:
            $ref: '#/components/schemas/MTLSSANEntry'
        hashes:
          type: array
          items:
            type: string
    ClientRouteAttachment:
      type: object
      properties:
        id:
          type: string
          format: uuid
        clientId:
          type: string
          format: uuid
        routeId:
          type: string
          format: uuid
        enableIpAllowlist:
          type: boolean
        enableApiKey:
          type: boolean
          description: Whether API key authentication is enabled for this attachment
        enableJwt:
          type: boolean
          description: Whether JWT authentication is enabled for this attachment
        enableBasicAuth:
          type: boolean
        enableMtls:
          type: boolean
        enableHeaderAuth:
          type: boolean
          description: Whether header-based authorization is enabled for this attachment
        rateLimitConfig:
          $ref: '#/components/schemas/RateLimitConfig'
        extAuth:
          $ref: '#/components/schemas/ExtAuthConfig'
        status:
          type: string
          enum:
            - pending_attach
            - pending_update
            - pending_detach
            - approved
            - active
            - removed
            - rejected
        client:
          $ref: '#/components/schemas/Client'
        route:
          $ref: '#/components/schemas/Route'
        creator:
          $ref: '#/components/schemas/User'
        pendingApproval:
          $ref: '#/components/schemas/Approval'
        createdBy:
          type: string
          format: uuid
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    AttachFromRouteRequest:
      type: object
      required:
        - clientId
      properties:
        clientId:
          type: string
          format: uuid
        enableIpAllowlist:
          type: boolean
          default: false
          description: Require client IP to be in allowlist
        enableApiKey:
          type: boolean
          default: false
          description: Require client to provide valid API key
        enableJwt:
          type: boolean
          default: false
          description: Require client to provide valid JWT token
        enableBasicAuth:
          type: boolean
          default: false
        enableMtls:
          type: boolean
          default: false
        enableHeaderAuth:
          type: boolean
          default: false
          description: Require client to satisfy header-based authorization rules
        rateLimitConfig:
          $ref: '#/components/schemas/RateLimitConfig'
        extAuth:
          $ref: '#/components/schemas/ExtAuthConfig'
    AttachFromClientRequest:
      type: object
      required:
        - routeId
        - projectId
      properties:
        routeId:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        enableIpAllowlist:
          type: boolean
          default: false
          description: Require client IP to be in allowlist
        enableApiKey:
          type: boolean
          default: false
          description: Require client to provide valid API key
        enableJwt:
          type: boolean
          default: false
          description: Require client to provide valid JWT token
        enableBasicAuth:
          type: boolean
          default: false
        enableMtls:
          type: boolean
          default: false
        enableHeaderAuth:
          type: boolean
          default: false
          description: Require client to satisfy header-based authorization rules
        rateLimitConfig:
          $ref: '#/components/schemas/RateLimitConfig'
        extAuth:
          $ref: '#/components/schemas/ExtAuthConfig'
    EffectiveIPEntry:
      type: object
      properties:
        cidr:
          type: string
          description: The IP CIDR
        clientId:
          type: string
          description: Client UUID that owns this IP
        clientName:
          type: string
          description: Name of the client
        description:
          type: string
          description: IP address description
    Approval:
      type: object
      properties:
        id:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        entityType:
          type: string
          enum:
            - route
            - client_attachment
        entityId:
          type: string
          format: uuid
        action:
          type: string
          enum:
            - create
            - update
            - delete
            - attach
            - detach
        configSnapshot:
          type: object
          nullable: true
          description: >-
            Snapshot of the proposed config. Raw JSON; shape varies by
            entityType/action.
        previousConfig:
          type: object
          nullable: true
          description: >-
            Snapshot of the prior config (update/delete only). Raw JSON; shape
            varies by entityType/action.
        submittedBy:
          type: string
          format: uuid
        submitter:
          $ref: '#/components/schemas/User'
        status:
          type: string
          enum:
            - pending
            - approved
            - rejected
            - cancelled
        stages:
          type: array
          items:
            $ref: '#/components/schemas/ApprovalStage'
        entityName:
          type: string
        domainName:
          type: string
          description: Hostname of the domain for route approvals (computed, not stored)
        changeDescription:
          type: string
          description: Optional human description of the changes
        aiReview:
          $ref: '#/components/schemas/AIReviewResult'
        createdAt:
          type: string
          format: date-time
    ApprovalStage:
      type: object
      properties:
        id:
          type: string
          format: uuid
        approvalId:
          type: string
          format: uuid
        order:
          type: integer
        requiredPermission:
          type: string
        requiredTeamId:
          type: string
          format: uuid
        requiredTeamName:
          type: string
        reviewedBy:
          type: string
          format: uuid
        reviewer:
          $ref: '#/components/schemas/User'
        status:
          type: string
          enum:
            - pending
            - approved
            - rejected
        comment:
          type: string
        minApprovers:
          type: integer
          description: Minimum number of approvers required for this stage (defaults to 1)
        reviews:
          type: array
          description: >-
            Individual reviewer decisions for multi-approver stages. Currently
            always empty in practice (not preloaded by the repository layer).
          items:
            $ref: '#/components/schemas/ApprovalStageReview'
        reviewedAt:
          type: string
          format: date-time
    ApprovalList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Approval'
        pagination:
          $ref: '#/components/schemas/Pagination'
    RejectRequest:
      type: object
      required:
        - comment
      properties:
        comment:
          type: string
          minLength: 1
    ApprovalDiffResponse:
      type: object
      required:
        - action
      properties:
        action:
          type: string
          enum:
            - create
            - update
            - delete
          description: Type of change being approved
        currentYaml:
          type: string
          description: Current HTTPRoute YAML (for update/delete)
        proposedYaml:
          type: string
          description: Proposed HTTPRoute YAML (for create/update)
        currentSecurityPolicyYaml:
          type: string
          description: Current SecurityPolicy YAML (if exists)
        proposedSecurityPolicyYaml:
          type: string
          description: Proposed SecurityPolicy YAML (if configured)
        currentBackendTrafficPolicyYaml:
          type: string
          description: Current BackendTrafficPolicy YAML (if exists)
        proposedBackendTrafficPolicyYaml:
          type: string
          description: Proposed BackendTrafficPolicy YAML (if configured)
        currentBackendYaml:
          type: string
          description: Current Backend CRD YAML for external service backends (if exists)
        proposedBackendYaml:
          type: string
          description: >-
            Proposed Backend CRD YAML for external service backends (if
            configured)
        currentEnvoyExtensionPolicyYaml:
          type: string
          description: Current EnvoyExtensionPolicy YAML (if exists)
        proposedEnvoyExtensionPolicyYaml:
          type: string
          description: >-
            Proposed EnvoyExtensionPolicy YAML for Lua/Wasm extensions (if
            configured)
        changeDescription:
          type: string
          description: Optional human description of the changes
        aiReview:
          $ref: '#/components/schemas/AIReviewResult'
    ApprovalPolicy:
      type: object
      properties:
        id:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        entityType:
          type: string
          enum:
            - route
            - client_attachment
          description: Type of entity this policy applies to
        action:
          type: string
          description: Specific action this policy applies to (optional)
        stages:
          type: array
          items:
            $ref: '#/components/schemas/PolicyStageTemplate'
          description: Approval stages defined by this policy
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    PolicyStageTemplate:
      type: object
      description: >-
        Stage definition as returned in ApprovalPolicy responses. The server
        serializes these fields snake_case (unlike PolicyStageInput, the request
        shape, which is camelCase).
      properties:
        order:
          type: integer
          description: Stage order (1-based)
        required_permission:
          type: string
          description: Permission required to approve this stage (e.g., "route.approve")
        team_scope:
          type: string
          enum:
            - any
            - other_team
            - submitter_team
          description: Which team members can approve this stage
        min_approvers:
          type: integer
          description: Minimum number of approvers required for this stage
    ApprovalPolicyInput:
      type: object
      required:
        - entityType
        - stages
      properties:
        entityType:
          type: string
          enum:
            - route
            - client_attachment
          description: Type of entity this policy applies to
        action:
          type: string
          description: Specific action this policy applies to (optional)
        stages:
          type: array
          items:
            $ref: '#/components/schemas/PolicyStageInput'
          description: Approval stages to configure
    PolicyStageInput:
      type: object
      required:
        - requiredPermission
        - teamScope
      properties:
        order:
          type: integer
          description: Stage order (auto-assigned if omitted)
        requiredPermission:
          type: string
          description: Permission required to approve this stage
        teamScope:
          type: string
          enum:
            - any
            - other_team
            - submitter_team
          description: Which team members can approve this stage
        minApprovers:
          type: integer
          description: Minimum number of approvers required
    K8sNamespace:
      type: object
      properties:
        name:
          type: string
        status:
          type: string
    K8sService:
      type: object
      properties:
        name:
          type: string
        namespace:
          type: string
        ports:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              port:
                type: integer
              protocol:
                type: string
    TestConnectionResult:
      type: object
      required:
        - ok
      properties:
        ok:
          type: boolean
        prometheusVersion:
          type: string
          description: Prometheus server version, present when ok is true
        error:
          type: string
          description: Failure reason, present when ok is false
    PromPoint:
      type: object
      properties:
        time:
          type: string
          format: date-time
        value:
          type: number
    MetricsTimeRange:
      type: object
      properties:
        start:
          type: string
          format: date-time
        end:
          type: string
          format: date-time
        step:
          type: string
          description: Resolved PromQL step (e.g. 15s, 1m)
    RpsByClass:
      type: object
      description: Requests-per-second time series bucketed by HTTP status class
      properties:
        2xx:
          type: array
          items:
            $ref: '#/components/schemas/PromPoint'
        3xx:
          type: array
          items:
            $ref: '#/components/schemas/PromPoint'
        4xx:
          type: array
          items:
            $ref: '#/components/schemas/PromPoint'
        5xx:
          type: array
          items:
            $ref: '#/components/schemas/PromPoint'
    LatencyPercentiles:
      type: object
      properties:
        p50:
          type: array
          items:
            $ref: '#/components/schemas/PromPoint'
        p95:
          type: array
          items:
            $ref: '#/components/schemas/PromPoint'
        p99:
          type: array
          items:
            $ref: '#/components/schemas/PromPoint'
    RouteMetricsResult:
      type: object
      properties:
        timeRange:
          $ref: '#/components/schemas/MetricsTimeRange'
        totalRequests:
          type: number
        errorRatePercent:
          type: number
        rps:
          $ref: '#/components/schemas/RpsByClass'
        latency:
          $ref: '#/components/schemas/LatencyPercentiles'
    TopRouteEntry:
      type: object
      properties:
        routeId:
          type: string
          format: uuid
        routeName:
          type: string
        value:
          type: number
    DomainMetricsResult:
      type: object
      properties:
        timeRange:
          $ref: '#/components/schemas/MetricsTimeRange'
        totalRequests:
          type: number
        errorRatePercent:
          type: number
        rps:
          $ref: '#/components/schemas/RpsByClass'
        latency:
          $ref: '#/components/schemas/LatencyPercentiles'
        topRoutesByRps:
          type: array
          items:
            $ref: '#/components/schemas/TopRouteEntry'
        topRoutesByErrorRate:
          type: array
          items:
            $ref: '#/components/schemas/TopRouteEntry'
    EffectiveSettings:
      type: object
      description: >-
        Resolved settings value for each field (DB override if set, else the env
        var/config default)
      properties:
        baseUrl:
          type: string
        jwtExpiry:
          type: string
        refreshTokenExpiry:
          type: string
        logLevel:
          type: string
          enum:
            - debug
            - info
            - warn
            - error
    SystemSettingsResponse:
      type: object
      properties:
        baseUrl:
          type: string
        jwtExpiry:
          type: string
          description: Go duration string (e.g. 24h, 1h30m)
        refreshTokenExpiry:
          type: string
          description: Go duration string (e.g. 168h, 720h)
        logLevel:
          type: string
          enum:
            - debug
            - info
            - warn
            - error
        effective:
          $ref: '#/components/schemas/EffectiveSettings'
    SystemSettingsInput:
      type: object
      description: >-
        All fields are optional; omitted fields are cleared to empty (falling
        back to the effective default).
      properties:
        baseUrl:
          type: string
        jwtExpiry:
          type: string
          description: Go duration string (e.g. 24h, 1h30m)
        refreshTokenExpiry:
          type: string
          description: Go duration string (e.g. 168h, 720h)
        logLevel:
          type: string
          enum:
            - debug
            - info
            - warn
            - error
    TopologyStatus:
      type: string
      description: Visual status used by the topology views
      enum:
        - deployed
        - pending
        - failed
        - draft
    SecurityFeatureFlags:
      type: object
      properties:
        ipAllowlist:
          type: boolean
        mtls:
          type: boolean
        apiKey:
          type: boolean
        jwt:
          type: boolean
        basicAuth:
          type: boolean
        headerAuth:
          type: boolean
        rateLimit:
          type: boolean
        extAuth:
          type: boolean
        oidc:
          type: boolean
        waf:
          type: boolean
    ClientCapabilities:
      type: object
      properties:
        apiKey:
          type: boolean
        jwt:
          type: boolean
        mtls:
          type: boolean
        ipAllowlistSize:
          type: integer
    ProjectTopologyResponse:
      type: object
      properties:
        domains:
          type: array
          items:
            $ref: '#/components/schemas/ProjectTopologyDomain'
        clients:
          type: array
          items:
            $ref: '#/components/schemas/ProjectTopologyClient'
        ips:
          type: array
          items:
            $ref: '#/components/schemas/TopologyIPRow'
    ProjectTopologyDomain:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        hostname:
          type: string
        securityMode:
          type: string
          enum:
            - general
            - client
        templateName:
          type: string
          nullable: true
        gatewayStatus:
          $ref: '#/components/schemas/TopologyStatus'
        counts:
          $ref: '#/components/schemas/ProjectTopologyDomainCount'
    ProjectTopologyDomainCount:
      type: object
      properties:
        routes:
          type: integer
        clientsAttached:
          type: integer
        routesWithIpAllowlist:
          type: integer
        routesWithMtls:
          type: integer
    ProjectTopologyClient:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        teamId:
          type: string
          format: uuid
        teamName:
          type: string
        capabilities:
          $ref: '#/components/schemas/ClientCapabilities'
        perDomain:
          type: object
          description: Map keyed by domain ID
          additionalProperties:
            $ref: '#/components/schemas/ProjectTopologyClientPerDomain'
    ProjectTopologyClientPerDomain:
      type: object
      properties:
        routeCount:
          type: integer
        aggregateStatus:
          $ref: '#/components/schemas/TopologyStatus'
    TopologyIPRow:
      type: object
      properties:
        cidr:
          type: string
        source:
          type: string
          enum:
            - route
            - client
        sourceRef:
          $ref: '#/components/schemas/TopologyIPSourceRef'
        reach:
          $ref: '#/components/schemas/TopologyIPReach'
        updatedAt:
          type: string
          format: date-time
    TopologyIPSourceRef:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
    TopologyIPReach:
      type: object
      properties:
        routeIds:
          type: array
          items:
            type: string
            format: uuid
        domainIds:
          type: array
          items:
            type: string
            format: uuid
    DomainTopologyResponse:
      type: object
      properties:
        domain:
          $ref: '#/components/schemas/DomainTopologyDomain'
        gateway:
          $ref: '#/components/schemas/DomainTopologyGateway'
        routes:
          type: array
          items:
            $ref: '#/components/schemas/DomainTopologyRoute'
        backends:
          type: array
          items:
            $ref: '#/components/schemas/DomainTopologyBackend'
        clients:
          type: array
          items:
            $ref: '#/components/schemas/DomainTopologyClient'
        attachments:
          type: array
          items:
            $ref: '#/components/schemas/DomainTopologyAttachment'
    DomainTopologyDomain:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        hostname:
          type: string
        securityMode:
          type: string
          enum:
            - general
            - client
        templateName:
          type: string
          nullable: true
    DomainTopologyGateway:
      type: object
      properties:
        status:
          $ref: '#/components/schemas/TopologyStatus'
        listenerPort:
          type: integer
        listenerProtocol:
          type: string
        tls:
          $ref: '#/components/schemas/DomainTopologyGatewayTLS'
        gatewayClass:
          type: string
    DomainTopologyGatewayTLS:
      type: object
      nullable: true
      properties:
        secretName:
          type: string
        secretNamespace:
          type: string
    DomainTopologyRoute:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        protocol:
          type: string
        matcherSummary:
          type: string
        method:
          type: string
          nullable: true
        status:
          $ref: '#/components/schemas/TopologyStatus'
        routeLevelSecurity:
          $ref: '#/components/schemas/SecurityFeatureFlags'
        backendIds:
          type: array
          items:
            type: string
        backendRoles:
          type: array
          items:
            $ref: '#/components/schemas/DomainTopologyBackendRole'
    DomainTopologyBackendRole:
      type: object
      properties:
        backendId:
          type: string
        role:
          type: string
          enum:
            - primary
            - fallback
            - mirror
        weight:
          type: integer
          nullable: true
    DomainTopologyBackend:
      type: object
      properties:
        id:
          type: string
        type:
          type: string
        service:
          type: string
          nullable: true
        namespace:
          type: string
          nullable: true
        address:
          type: string
          nullable: true
        addressType:
          type: string
          nullable: true
        port:
          type: integer
        hitCount:
          type: integer
    DomainTopologyClient:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        teamId:
          type: string
          format: uuid
        teamName:
          type: string
        capabilities:
          $ref: '#/components/schemas/ClientCapabilities'
    DomainTopologyAttachment:
      type: object
      properties:
        id:
          type: string
          format: uuid
        clientId:
          type: string
          format: uuid
        routeId:
          type: string
          format: uuid
        status:
          $ref: '#/components/schemas/TopologyStatus'
        enforced:
          $ref: '#/components/schemas/SecurityFeatureFlags'
        hasRateLimit:
          type: boolean
        hasExtAuth:
          type: boolean
    AuditLog:
      type: object
      properties:
        id:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        userId:
          type: string
          format: uuid
        username:
          type: string
        action:
          type: string
        resourceType:
          type: string
        resourceId:
          type: string
          format: uuid
        resourceName:
          type: string
        details:
          type: object
        ipAddress:
          type: string
        userAgent:
          type: string
        createdAt:
          type: string
          format: date-time
    AuditLogList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/AuditLog'
        pagination:
          $ref: '#/components/schemas/Pagination'
    AuditCleanupRequest:
      type: object
      required:
        - days
      properties:
        days:
          type: integer
          minimum: 1
          description: Delete audit logs older than this many days
          example: 90
    AuditCleanupResponse:
      type: object
      properties:
        deleted:
          type: integer
          description: Number of audit log entries deleted
        message:
          type: string
          description: Human-readable result message
          example: Deleted 42 audit logs older than 90 days
    AIStatus:
      type: object
      properties:
        enabled:
          type: boolean
        provider:
          type: string
          enum:
            - anthropic
            - openai
            - gemini
            - deepseek
            - openai_compatible
    AIGenerateRequest:
      type: object
      required:
        - mode
        - input
      properties:
        mode:
          type: string
          enum:
            - natural_language
            - manifest_import
        input:
          type: string
          description: User prompt or YAML manifest
        formatHint:
          type: string
          enum:
            - ingress
            - istio
            - kong
          description: Source manifest dialect hint, used when mode is manifest_import
    AIYamlSet:
      type: object
      description: Set of YAML strings for different resource types
      properties:
        httpRoute:
          type: string
          description: HTTPRoute or GRPCRoute YAML
        securityPolicy:
          type: string
          description: SecurityPolicy YAML
        backendTrafficPolicy:
          type: string
          description: BackendTrafficPolicy YAML
        envoyExtensionPolicy:
          type: string
          description: EnvoyExtensionPolicy YAML
        backend:
          type: string
          description: Backend CRD YAML
    AIChatRequest:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          maxLength: 10000
          description: User message to the AI assistant
        context:
          $ref: '#/components/schemas/AIChatContext'
        history:
          type: array
          maxItems: 50
          items:
            $ref: '#/components/schemas/AIChatMessage'
          description: Previous conversation messages
    AIChatContext:
      type: object
      properties:
        type:
          type: string
          enum:
            - route
            - domain
          description: Type of entity the user is currently configuring
        route:
          type: object
          description: Current route configuration context
        domain:
          type: object
          description: Current domain configuration context
    AIChatMessage:
      type: object
      properties:
        role:
          type: string
          enum:
            - user
            - assistant
        content:
          type: string
    AIReviewRequest:
      type: object
      required:
        - action
      description: At least one of proposedYaml or currentYaml is required.
      properties:
        action:
          type: string
          enum:
            - create
            - update
            - delete
          description: Type of route change being reviewed
        description:
          type: string
          description: Optional human description of the changes
        proposedYaml:
          $ref: '#/components/schemas/AIYamlSet'
        currentYaml:
          $ref: '#/components/schemas/AIYamlSet'
    AIReviewResult:
      type: object
      required:
        - summary
      properties:
        summary:
          type: string
          description: Plain language summary of the changes
        risks:
          type: array
          items:
            $ref: '#/components/schemas/AIReviewNote'
          description: Potential risks and issues
        securityNotes:
          type: array
          items:
            $ref: '#/components/schemas/AIReviewNote'
          description: Security-related observations
        suggestions:
          type: array
          items:
            type: string
          description: Actionable improvement suggestions
        configHighlights:
          type: array
          items:
            type: string
          description: Key facts about the configuration
    AIReviewNote:
      type: object
      required:
        - severity
        - message
      properties:
        severity:
          type: string
          enum:
            - warning
            - info
          description: Severity level of the note
        message:
          type: string
          description: Description of the risk or observation
    WafPolicyConfig:
      type: object
      required:
        - mode
      description: WAF (Web Application Firewall) configuration using coraza-proxy-wasm
      properties:
        mode:
          type: string
          enum:
            - block
            - detect
          description: WAF mode - block malicious requests or detect-only (log)
        rulesets:
          type: array
          items:
            type: string
          description: List of enabled rulesets (e.g., owasp-crs)
        anomalyThreshold:
          type: integer
          minimum: 1
          description: Anomaly score threshold before blocking (default 5)
        paranoiaLevel:
          type: integer
          minimum: 1
          maximum: 4
          description: OWASP CRS paranoia level 1-4 (default 1)
        disabledRuleIDs:
          type: array
          items:
            type: integer
          description: Specific rule IDs to disable
        customDirectives:
          type: array
          items:
            type: string
          description: Raw SecRule directives (advanced escape hatch)
    WafPolicy:
      type: object
      description: WAF policy for a route
      properties:
        id:
          type: string
          format: uuid
        routeId:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        config:
          $ref: '#/components/schemas/WafPolicyConfig'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    EnvoyExtensionPolicyInput:
      type: object
      description: Envoy extension policy input for Lua, Wasm, and ext_proc extensions
      properties:
        lua:
          $ref: '#/components/schemas/LuaExtensionConfig'
        wasm:
          $ref: '#/components/schemas/WasmExtensionConfig'
        extProc:
          $ref: '#/components/schemas/ExtProcExtensionConfig'
    LuaExtensionConfig:
      type: object
      required:
        - type
      description: Lua extension configuration
      properties:
        type:
          type: string
          enum:
            - Inline
            - ValueRef
          description: Lua script source type
        inline:
          type: string
          description: Inline Lua script (when type is Inline)
        valueRef:
          $ref: '#/components/schemas/ValueRef'
    WasmExtensionConfig:
      type: object
      required:
        - name
        - code
      description: Wasm extension configuration
      properties:
        name:
          type: string
          description: Wasm filter name
        rootID:
          type: string
          description: Wasm root ID (optional)
        code:
          $ref: '#/components/schemas/WasmCodeSource'
        config:
          type: string
          description: JSON configuration for the Wasm module
    WasmCodeSource:
      type: object
      required:
        - type
      description: Wasm code source
      properties:
        type:
          type: string
          enum:
            - HTTP
            - Image
          description: Source type
        http:
          $ref: '#/components/schemas/WasmHTTPSource'
        image:
          $ref: '#/components/schemas/WasmImageSource'
    WasmHTTPSource:
      type: object
      required:
        - url
        - sha256
      description: HTTP source for Wasm module
      properties:
        url:
          type: string
          description: URL to download the Wasm module
        sha256:
          type: string
          description: SHA256 checksum of the module (64-character hex string)
    WasmImageSource:
      type: object
      required:
        - url
      description: OCI image source for Wasm module
      properties:
        url:
          type: string
          description: OCI image URL (e.g., "oci://ghcr.io/example/wasm:v1.0")
        sha256:
          type: string
          description: SHA256 checksum (optional for images)
        pullSecret:
          $ref: '#/components/schemas/ValueRef'
          description: Reference to Secret containing registry credentials
    SSOPublicConfig:
      type: object
      properties:
        enabled:
          type: boolean
        providerName:
          type: string
        forceSSO:
          type: boolean
        allowedDomains:
          type: array
          items:
            type: string
    SSOConfig:
      type: object
      properties:
        id:
          type: string
          format: uuid
        enabled:
          type: boolean
        providerName:
          type: string
        issuerUrl:
          type: string
        clientId:
          type: string
        scopes:
          type: array
          items:
            type: string
        allowedDomains:
          type: array
          items:
            type: string
        allowedEmails:
          type: array
          items:
            type: string
            format: email
        autoRegister:
          type: boolean
        forceSSO:
          type: boolean
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    SSOConfigInput:
      type: object
      required:
        - providerName
        - issuerUrl
        - clientId
        - clientSecret
      properties:
        providerName:
          type: string
          description: Identity provider name (e.g., google, okta)
        issuerUrl:
          type: string
          description: OIDC issuer URL
        clientId:
          type: string
          description: OAuth2 client ID
        clientSecret:
          type: string
          description: OAuth2 client secret
        scopes:
          type: array
          items:
            type: string
          description: OAuth2 scopes (default - openid, email, profile)
        allowedDomains:
          type: array
          items:
            type: string
          description: Restrict SSO to these email domains
        allowedEmails:
          type: array
          items:
            type: string
            format: email
          description: Restrict SSO to these specific email addresses
        autoRegister:
          type: boolean
          description: Auto-create users on first SSO login
        forceSSO:
          type: boolean
          description: Force SSO for non-owner users
    Notification:
      type: object
      properties:
        id:
          type: string
          format: uuid
        userId:
          type: string
          format: uuid
        type:
          type: string
          description: Notification type (e.g., mention)
        title:
          type: string
        link:
          type: string
        isRead:
          type: boolean
        createdAt:
          type: string
          format: date-time
    NotificationList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Notification'
        pagination:
          $ref: '#/components/schemas/Pagination'
    DNSProviderCredential:
      type: object
      description: >-
        Platform-global DNS provider credential (owner-only). Never includes
        credential material.
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        providerType:
          type: string
          description: DNS provider identifier (e.g. cloudflare, route53).
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    DNSProviderCredentialList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/DNSProviderCredential'
    CreateDNSCredentialRequest:
      type: object
      required:
        - name
        - providerType
        - credentials
      properties:
        name:
          type: string
        providerType:
          type: string
          description: DNS provider identifier (e.g. cloudflare, route53).
        credentials:
          type: object
          additionalProperties:
            type: string
          description: >-
            Provider-specific plaintext credential values (e.g. `{"apiToken":
            "..."}`). Encrypted at rest; never returned in any response.
    UpdateDNSCredentialRequest:
      type: object
      description: >-
        Both fields are optional. Omitting `credentials` leaves the existing
        encrypted values untouched.
      properties:
        name:
          type: string
        credentials:
          type: object
          additionalProperties:
            type: string
          description: >-
            Provider-specific plaintext credential values to rotate. Encrypted
            at rest; never returned in any response.
    IssuerConfig:
      type: object
      description: >-
        Polymorphic issuer configuration. The self_signed_ca fields apply only
        when `type` is `self_signed_ca`; the acme fields apply only when `type`
        is `acme`. Never includes key material -- private keys and ACME
        account/EAB secrets live in cert-manager-managed Kubernetes Secrets, not
        in this row.
      properties:
        commonName:
          type: string
        keyAlgorithm:
          type: string
        keySize:
          type: integer
        durationDays:
          type: integer
        caSecretName:
          type: string
          description: Name of the cert-manager-managed CA Secret in the control cluster.
        server:
          type: string
          description: ACME server URL.
        email:
          type: string
        eabKeyId:
          type: string
        dnsCredentialId:
          type: string
          format: uuid
        issuerName:
          type: string
          description: Name of the resolved cert-manager Issuer.
        accountSecretName:
          type: string
        solverSecretName:
          type: string
        eabSecretName:
          type: string
    CertificateIssuer:
      type: object
      description: >-
        Platform-global certificate issuer (self-signed CA or ACME), owner-only.
        Never includes key material.
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        type:
          type: string
          enum:
            - self_signed_ca
            - acme
        status:
          type: string
          enum:
            - pending
            - ready
            - error
        statusMessage:
          type: string
        config:
          $ref: '#/components/schemas/IssuerConfig'
        createdBy:
          type: string
          format: uuid
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    CertificateIssuerList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/CertificateIssuer'
    CertificateIssuerStatus:
      type: object
      properties:
        status:
          type: string
          enum:
            - pending
            - ready
            - error
        statusMessage:
          type: string
    CreateCertificateIssuerRequest:
      type: object
      description: >-
        Discriminated by `type`. self_signed_ca requires `commonName`; acme
        requires `server`, `email`, and `dnsCredentialId`.
      required:
        - type
        - name
      properties:
        type:
          type: string
          enum:
            - self_signed_ca
            - acme
        name:
          type: string
        commonName:
          type: string
          description: Required when type is self_signed_ca.
        keyAlgorithm:
          type: string
          description: Defaults to RSA when type is self_signed_ca.
        keySize:
          type: integer
          description: Defaults to 4096 when type is self_signed_ca.
        durationDays:
          type: integer
          description: Defaults to 3650 when type is self_signed_ca.
        server:
          type: string
          description: ACME server URL. Required when type is acme.
        email:
          type: string
          description: Required when type is acme.
        eabKeyId:
          type: string
          description: >-
            ACME External Account Binding key ID, if the ACME server requires
            EAB (e.g. ZeroSSL).
        eabHmacKey:
          type: string
          description: >-
            ACME External Account Binding HMAC key, plaintext in the request
            only -- stored as a cert-manager Secret, never persisted in this row
            or returned.
        dnsCredentialId:
          type: string
          format: uuid
          description: >-
            DNS provider credential used for the ACME DNS-01 solver. Required
            when type is acme.
    IssuerProjectGrant:
      type: object
      description: Records that a certificate issuer has been made visible to a project.
      properties:
        id:
          type: string
          format: uuid
        issuerId:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        createdBy:
          type: string
          format: uuid
        createdAt:
          type: string
          format: date-time
    IssuerProjectGrantList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/IssuerProjectGrant'
    CreateIssuerProjectGrantRequest:
      type: object
      required:
        - projectId
      properties:
        projectId:
          type: string
          format: uuid
    RouteVersion:
      type: object
      properties:
        id:
          type: string
          format: uuid
        routeId:
          type: string
          format: uuid
        version:
          type: integer
        configSnapshot:
          type: object
          description: Full route configuration at this version
        routeDescription:
          type: string
        protocol:
          type: string
          enum:
            - http
            - grpc
        securityMode:
          type: string
          enum:
            - general
            - client
        changeDescription:
          type: string
        approvalId:
          type: string
          format: uuid
        deployedBy:
          type: string
          format: uuid
        deployer:
          $ref: '#/components/schemas/User'
        createdAt:
          type: string
          format: date-time
    RouteVersionList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/RouteVersion'
        total:
          type: integer
        page:
          type: integer
        limit:
          type: integer
    ApprovalComment:
      type: object
      properties:
        id:
          type: string
          format: uuid
        approvalId:
          type: string
          format: uuid
        userId:
          type: string
          format: uuid
        body:
          type: string
        createdAt:
          type: string
          format: date-time
        user:
          $ref: '#/components/schemas/User'
    CreateCommentRequest:
      type: object
      required:
        - body
      properties:
        body:
          type: string
          maxLength: 10000
    CommentList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/ApprovalComment'
        total:
          type: integer
    EnrichedManagedCertificate:
      type: object
      description: >-
        ManagedCertificate plus resolved issuer name/type, Phase 3a distribution
        sync state, and the domains referencing this certificate. Metadata only
        -- distribution carries a fingerprint hash (never key/certificate
        material), and domains carry id+hostname only (no TLS config or gateway
        wiring).
      properties:
        id:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        name:
          type: string
        issuerId:
          type: string
          format: uuid
        usage:
          type: string
          enum:
            - server
            - client
        dnsNames:
          type: array
          items:
            type: string
        status:
          type: string
          enum:
            - pending
            - issuing
            - ready
            - error
        statusMessage:
          type: string
        fingerprint:
          type: string
        notAfter:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time
        keyMode:
          type: string
          enum:
            - managed
            - csr
          description: >-
            managed (cert-manager generates and holds the leaf private key,
            default) or csr (the caller supplied a CSR and holds the private key
            itself -- export is not applicable).
        subject:
          type: string
          description: Set for client-usage certificates.
        uriSans:
          type: array
          items:
            type: string
          description: >-
            URI Subject Alternative Names, applicable to client-usage
            certificates.
        exportAvailable:
          type: boolean
          description: >-
            Whether the CURRENT caller has an approved, unconsumed, unexpired
            export grant for this certificate (per-cert AND per-user).
        issuerName:
          type: string
        issuerType:
          type: string
        distribution:
          nullable: true
          type: object
          description: >-
            Null when the certdist controller has never pushed this
            certificate's Secret.
          properties:
            status:
              type: string
              enum:
                - pending
                - synced
                - error
            lastPushedFingerprint:
              type: string
            message:
              type: string
            lastSyncedAt:
              type: string
              format: date-time
        domains:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                format: uuid
              hostname:
                type: string
    EnrichedManagedCertificateList:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/EnrichedManagedCertificate'
        pagination:
          $ref: '#/components/schemas/Pagination'
    Labels:
      type: object
      additionalProperties:
        type: string
      description: Arbitrary key/value labels (Kubernetes-style) attached to a resource.
    VersionStatus:
      type: string
      enum:
        - supported
        - untested
        - unknown
      description: >
        - `supported`: the detected (envoyGateway, gatewayAPI) pair is in
        `supportedPairs`.

        - `untested`: both versions were detected but the pair is not one
        FastGateway has tested against.

        - `unknown`: detection failed for one or both components.
    ProbeResult:
      type: object
      description: The detected version of one component (Envoy Gateway or Gateway API).
      properties:
        version:
          type: string
          description: Detected version string (e.g. "1.8.4"), empty if detection failed.
        image:
          type: string
          description: Container image the version was detected from, if applicable.
        source:
          type: string
          description: How the version was detected (e.g. deployment image, CRD version).
        detected:
          type: boolean
          description: Whether a version was successfully detected.
        error:
          type: string
          nullable: true
          description: Error message if detection failed; null otherwise.
    VersionPair:
      type: object
      description: A tested (Envoy Gateway, Gateway API) version combination.
      properties:
        envoyGateway:
          type: string
          example: 1.8.4
        gatewayAPI:
          type: string
          example: 1.5.1
    VersionInfo:
      type: object
      description: >-
        Detected Envoy Gateway / Gateway API versions for a project's cluster,
        with compatibility classification against the versions FastGateway has
        been tested with.
      properties:
        status:
          $ref: '#/components/schemas/VersionStatus'
        envoyGateway:
          $ref: '#/components/schemas/ProbeResult'
        gatewayAPI:
          $ref: '#/components/schemas/ProbeResult'
        supportedPairs:
          type: array
          items:
            $ref: '#/components/schemas/VersionPair'
        checkedAt:
          type: string
          format: date-time
        cacheExpiresAt:
          type: string
          format: date-time
    NamespaceCapability:
      type: string
      enum:
        - deploy_gateway
        - backend_service
        - tls_secret
      description: >
        - `deploy_gateway`: Gateway / HTTPRoute / GRPCRoute can be deployed in
        this namespace.

        - `backend_service`: HTTPRoutes can forward to Services in this
        namespace.

        - `tls_secret`: Gateway can mount TLS Secrets stored in this namespace.
    UpdateProjectNamespaceRequest:
      type: object
      required:
        - capabilities
      properties:
        capabilities:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/NamespaceCapability'
    ExtProcBackendRef:
      type: object
      required:
        - name
        - namespace
        - port
      description: gRPC service to send request/response traffic to for ext_proc processing
      properties:
        name:
          type: string
          description: Service name of the ext_proc gRPC backend
        namespace:
          type: string
          description: Namespace of the ext_proc gRPC backend
        port:
          type: integer
          minimum: 1
          maximum: 65535
          description: Port of the ext_proc gRPC backend
    ExtProcBodyMode:
      type: object
      description: Body processing mode for a request or response phase
      properties:
        body:
          type: string
          enum:
            - None
            - Buffered
            - Streamed
          description: How the body is sent to the ext_proc processor
    ExtProcProcessingMode:
      type: object
      description: Controls which request/response parts are sent to the ext_proc processor
      properties:
        request:
          $ref: '#/components/schemas/ExtProcBodyMode'
        response:
          $ref: '#/components/schemas/ExtProcBodyMode'
    ExtProcExtensionConfig:
      type: object
      required:
        - backendRef
      description: >-
        External processing (ext_proc) extension configuration - sends
        request/response traffic to a gRPC processor service
      properties:
        backendRef:
          $ref: '#/components/schemas/ExtProcBackendRef'
        processingMode:
          $ref: '#/components/schemas/ExtProcProcessingMode'
        failOpen:
          type: boolean
          default: false
          description: >-
            If true, traffic is allowed through when the ext_proc server is
            unavailable
    EnvoyExtensionPolicyConfig:
      type: object
      description: Lua, Wasm, and ext_proc extension configuration stored for a route
      properties:
        lua:
          $ref: '#/components/schemas/LuaExtensionConfig'
        wasm:
          $ref: '#/components/schemas/WasmExtensionConfig'
        extProc:
          $ref: '#/components/schemas/ExtProcExtensionConfig'
    DefaultBackend:
      type: object
      required:
        - port
      description: |
        Backend applied to every route parsed from the spec. Exactly one of
        (service+namespace) or address must be provided.
      properties:
        service:
          type: string
          description: >-
            Kubernetes Service name (mutually exclusive with address; requires
            namespace)
        namespace:
          type: string
          description: Kubernetes namespace of the service (required when service is set)
        address:
          type: string
          description: >-
            External FQDN or IP address (mutually exclusive with
            service+namespace)
        port:
          type: integer
          description: Backend port
    OpenAPIImportRequest:
      type: object
      required:
        - spec
        - defaultBackend
      description: Request body for importing routes from an OpenAPI spec
      properties:
        spec:
          type: string
          description: Raw OpenAPI 3.0/3.1 spec content (JSON or YAML)
        defaultBackend:
          $ref: '#/components/schemas/DefaultBackend'
    ParsedRoute:
      type: object
      required:
        - name
        - protocol
        - securityMode
        - config
      description: >-
        A route generated from one OpenAPI operation, ready for review before
        creation
      properties:
        name:
          type: string
        description:
          type: string
        protocol:
          type: string
          enum:
            - http
          description: Always "http" for v1 of the importer
        securityMode:
          type: string
          enum:
            - general
          description: Always "general" for v1 of the importer
        config:
          $ref: '#/components/schemas/RouteConfig'
        tag:
          type: string
          description: OpenAPI tag the source operation belonged to, if any
    ImportWarning:
      type: object
      required:
        - level
        - source
        - message
      description: Parser-level info or a per-operation skip surfaced to the importing user
      properties:
        level:
          type: string
          enum:
            - info
            - warning
        source:
          type: string
          description: Where the warning originated, e.g. "POST /webhook" or "spec"
        message:
          type: string
    Rename:
      type: object
      required:
        - original
        - final
        - reason
      description: >-
        Records when a generated route name was changed from its source-derived
        form
      properties:
        original:
          type: string
        final:
          type: string
        reason:
          type: string
          enum:
            - duplicate
            - sanitized
            - truncated
    SpecInfo:
      type: object
      required:
        - title
        - version
        - format
      description: Summary of the input spec for UI display
      properties:
        title:
          type: string
        version:
          type: string
        format:
          type: string
          enum:
            - openapi-3.0
            - openapi-3.1
    OpenAPIImportResponse:
      type: object
      required:
        - routes
        - warnings
        - renames
        - specInfo
      properties:
        routes:
          type: array
          items:
            $ref: '#/components/schemas/ParsedRoute'
        warnings:
          type: array
          items:
            $ref: '#/components/schemas/ImportWarning'
        renames:
          type: array
          items:
            $ref: '#/components/schemas/Rename'
        specInfo:
          $ref: '#/components/schemas/SpecInfo'
    ImportError:
      type: object
      required:
        - error
        - message
      description: Error response for the OpenAPI import endpoint
      properties:
        error:
          type: string
          enum:
            - invalid_request
            - openapi_parse_failed
            - spec_too_large
        message:
          type: string
    ApprovalStageReview:
      type: object
      description: An individual reviewer decision on a multi-approver stage
      properties:
        id:
          type: string
          format: uuid
        stageId:
          type: string
          format: uuid
        reviewerId:
          type: string
          format: uuid
        reviewer:
          $ref: '#/components/schemas/User'
        decision:
          type: string
          enum:
            - approved
            - rejected
        createdAt:
          type: string
          format: date-time
    AuthorizationHeaderMatch:
      type: object
      required:
        - name
        - values
      description: Required header match rule for general-mode authorization
      properties:
        name:
          type: string
          description: Header name to match
        values:
          type: array
          items:
            type: string
          description: Allowed values for the header
    RouteResponse:
      type: object
      description: >-
        Route returned by createRoute/updateRoute, with any non-fatal warnings
        generated while assembling the route.
      allOf:
        - $ref: '#/components/schemas/Route'
        - type: object
          properties:
            warnings:
              type: array
              items:
                type: string
              description: >-
                Non-fatal warnings (e.g. backend TLS configuration,
                direct-response percentage) generated while creating/updating
                the route
    EnvoyExtensionPolicy:
      type: object
      description: >-
        Envoy Gateway EnvoyExtensionPolicy resource (Lua, Wasm, and/or ext_proc
        extensions configured on a route)
      properties:
        id:
          type: string
          format: uuid
        routeId:
          type: string
          format: uuid
        domainId:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        config:
          $ref: '#/components/schemas/EnvoyExtensionPolicyConfig'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    RouteWithPolicies:
      type: object
      description: >
        Route detail response (GET /routes/{routeId} only) - the plain Route
        plus

        its associated policies, each only present when configured for the
        route.
      allOf:
        - $ref: '#/components/schemas/Route'
        - type: object
          properties:
            securityPolicy:
              $ref: '#/components/schemas/SecurityPolicy'
            backendTrafficPolicy:
              $ref: '#/components/schemas/BackendTrafficPolicy'
            extensionPolicy:
              $ref: '#/components/schemas/EnvoyExtensionPolicy'
            wafPolicy:
              $ref: '#/components/schemas/WafPolicy'
    APIKeyClientResourceYAMLs:
      type: object
      required:
        - clientId
        - clientName
        - httpRouteYaml
        - securityPolicyYaml
      description: >-
        Kubernetes resources generated for a single API-key client attachment,
        with secrets redacted
      properties:
        clientId:
          type: string
          format: uuid
        clientName:
          type: string
        httpRouteYaml:
          type: string
          description: Per-client HTTPRoute/GRPCRoute Kubernetes YAML
        securityPolicyYaml:
          type: string
          description: Per-client SecurityPolicy Kubernetes YAML
        backendTrafficPolicyYaml:
          type: string
          description: Per-client BackendTrafficPolicy Kubernetes YAML (if configured)
        envoyExtensionPolicyYaml:
          type: string
          description: Per-client EnvoyExtensionPolicy Kubernetes YAML (if configured)
    CheckConflictsRequest:
      type: object
      required:
        - match
      description: >-
        Request body for checking whether a route matcher conflicts with
        existing routes in the domain
      properties:
        match:
          $ref: '#/components/schemas/RouteMatch'
        excludeRouteId:
          type: string
          format: uuid
          description: >-
            Route ID to exclude from conflict checking (e.g., when checking
            conflicts while editing an existing route)
    ConflictResult:
      type: object
      description: An existing route whose matcher conflicts with the one being checked
      properties:
        routeId:
          type: string
          format: uuid
        routeName:
          type: string
    CheckConflictsResponse:
      type: object
      properties:
        conflicts:
          type: array
          items:
            $ref: '#/components/schemas/ConflictResult'
    AttachClientCertificateRequest:
      type: object
      required:
        - certificateId
      properties:
        certificateId:
          type: string
          format: uuid
          description: >-
            A managed, usage=client certificate (see POST
            /projects/{projectId}/certificates) in a project the client's team
            has access to.
    ManagedCertificate:
      type: object
      description: >-
        Project-scoped managed certificate issued from a granted certificate
        issuer. Metadata only -- never includes key or certificate material,
        which lives in a cert-manager-managed Kubernetes Secret, never in this
        row or response.
      properties:
        id:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        name:
          type: string
        issuerId:
          type: string
          format: uuid
        usage:
          type: string
          enum:
            - server
            - client
        dnsNames:
          type: array
          items:
            type: string
        status:
          type: string
          enum:
            - pending
            - issuing
            - ready
            - error
        statusMessage:
          type: string
        fingerprint:
          type: string
        notAfter:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time
        keyMode:
          type: string
          enum:
            - managed
            - csr
          description: >-
            managed (cert-manager generates and holds the leaf private key,
            default) or csr (the caller supplied a CSR and holds the private key
            itself -- export is not applicable).
        subject:
          type: string
          description: Set for client-usage certificates.
        uriSans:
          type: array
          items:
            type: string
          description: >-
            URI Subject Alternative Names, applicable to client-usage
            certificates.
        exportAvailable:
          type: boolean
          description: >-
            Whether the CURRENT caller has an approved, unconsumed, unexpired
            export grant for this certificate (per-cert AND per-user). Always
            false on the Create response.
    AttachableCertificateList:
      type: object
      description: >-
        Managed client-usage certificates a given client could attach --
        usage=client, status=ready, in a project the client's team has a role
        in, and not already attached to any client.
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/ManagedCertificate'
    CreateManagedCertificateRequest:
      type: object
      description: >-
        DNSNames applies to server-usage certificates; subject applies to
        client-usage certificates.
      required:
        - name
        - issuerId
        - usage
      properties:
        name:
          type: string
        issuerId:
          type: string
          format: uuid
        usage:
          type: string
          enum:
            - server
            - client
        dnsNames:
          type: array
          items:
            type: string
          description: Required when usage is server.
        subject:
          type: string
          description: Required when usage is client.
        keyAlgorithm:
          type: string
          description: Defaults to RSA.
        keySize:
          type: integer
          description: Defaults to 2048.
        durationDays:
          type: integer
          description: Defaults to 90.
        keyMode:
          type: string
          enum:
            - managed
            - csr
          description: >-
            Defaults to managed (cert-manager generates and holds the leaf
            private key). Set to csr to have cert-manager only sign a
            caller-supplied CSR, in which case csr is required and the private
            key never exists server-side (so export is not applicable to the
            resulting certificate).
        uriSans:
          type: array
          items:
            type: string
          description: >-
            URI Subject Alternative Names, applicable to client-usage
            certificates.
        csr:
          type: string
          description: >-
            Caller-supplied PEM-encoded certificate signing request. Required
            when keyMode is csr; ignored otherwise.
    CreateManagedCertificateResponse:
      type: object
      description: >-
        Returned with 201 when the certificate was issued immediately (project
        has approvals disabled), or 202 when it opened an approval and is still
        pending.
      properties:
        certificate:
          $ref: '#/components/schemas/ManagedCertificate'
        approvalId:
          type: string
          format: uuid
          nullable: true
          description: >-
            Null when the certificate was issued via the fast path (no approval
            gate configured for this project).
    ManagedCertificateStatus:
      type: object
      description: >-
        Live cert-manager issuance status of a managed certificate, from the
        underlying `Certificate` resource's `Ready` condition.
      properties:
        status:
          type: string
          enum:
            - pending
            - issuing
            - ready
            - error
        message:
          type: string
        notAfter:
          type: string
          format: date-time
    CertificateDistribution:
      type: object
      description: >-
        Phase 3a distribution state of a managed certificate's Secret into the
        project's tenant cluster. Carries only a fingerprint hash -- never key
        or certificate material.
      properties:
        status:
          type: string
          enum:
            - pending
            - synced
            - error
        lastPushedFingerprint:
          type: string
        message:
          type: string
        lastSyncedAt:
          type: string
          format: date-time
    RequestExportResponse:
      type: object
      description: >-
        Returned by POST .../export with 202, whether the export was submitted
        for approval or (approvals disabled for this project) the grant was
        minted immediately.
      properties:
        approvalId:
          type: string
          format: uuid
          nullable: true
          description: >-
            Null when the export grant was minted immediately (no approval gate
            configured for this project).
