openapi: 3.1.0

info:
  title: Veylant IA Proxy API
  version: 1.0.0
  description: |
    Enterprise AI governance gateway — OpenAI-compatible proxy with PII anonymization,
    RBAC, audit logging, dynamic routing, GDPR/EU AI Act compliance, and cost tracking.

    All `/v1/*` endpoints require a Bearer JWT token issued by Keycloak.
    The token must contain `tenant_id`, `user_id`, and `roles` claims.

  contact:
    name: Veylant IA Support
    url: https://veylant.ai

servers:
  - url: http://localhost:8090
    description: Local development
  - url: https://api.veylant.ai
    description: Production

security:
  - BearerAuth: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Keycloak-issued JWT. Claims must include `tenant_id`, `user_id`, `roles`.

  schemas:
    # ─── Chat ────────────────────────────────────────────────────────────────
    Message:
      type: object
      required: [role, content]
      properties:
        role:
          type: string
          enum: [system, user, assistant]
        content:
          type: string

    ChatRequest:
      type: object
      required: [model, messages]
      properties:
        model:
          type: string
          example: gpt-4o
        messages:
          type: array
          items:
            $ref: '#/components/schemas/Message'
          minItems: 1
        stream:
          type: boolean
          default: false
        temperature:
          type: number
          minimum: 0
          maximum: 2
        max_tokens:
          type: integer
          minimum: 1

    Choice:
      type: object
      properties:
        index:
          type: integer
        message:
          $ref: '#/components/schemas/Message'
        finish_reason:
          type: string
          enum: [stop, length, tool_calls, content_filter]

    Usage:
      type: object
      properties:
        prompt_tokens:
          type: integer
        completion_tokens:
          type: integer
        total_tokens:
          type: integer

    ChatResponse:
      type: object
      properties:
        id:
          type: string
        object:
          type: string
          example: chat.completion
        created:
          type: integer
        model:
          type: string
        choices:
          type: array
          items:
            $ref: '#/components/schemas/Choice'
        usage:
          $ref: '#/components/schemas/Usage'

    # ─── PII ─────────────────────────────────────────────────────────────────
    PIIEntity:
      type: object
      properties:
        type:
          type: string
          example: EMAIL_ADDRESS
        text:
          type: string
        score:
          type: number
        start:
          type: integer
        end:
          type: integer

    PIIAnalyzeRequest:
      type: object
      required: [text]
      properties:
        text:
          type: string
        language:
          type: string
          default: fr

    PIIAnalyzeResponse:
      type: object
      properties:
        original_text:
          type: string
        anonymized_text:
          type: string
        entities:
          type: array
          items:
            $ref: '#/components/schemas/PIIEntity'
        processing_time_ms:
          type: integer

    # ─── Routing Policies ────────────────────────────────────────────────────
    Condition:
      type: object
      required: [field, operator, value]
      properties:
        field:
          type: string
          example: department
          description: Field to evaluate (department, role, model, sensitivity, user_id)
        operator:
          type: string
          enum: [eq, neq, in, not_in, gte, lte, contains, regex]
        value:
          description: Comparison value (string or array for in/not_in operators)

    Action:
      type: object
      required: [provider]
      properties:
        provider:
          type: string
          example: openai
        model:
          type: string
          description: Override the model sent upstream (optional)
          example: gpt-4o-mini

    RoutingRule:
      type: object
      properties:
        id:
          type: string
        tenant_id:
          type: string
        name:
          type: string
        description:
          type: string
        priority:
          type: integer
          description: Lower value = evaluated first
        is_enabled:
          type: boolean
        conditions:
          type: array
          items:
            $ref: '#/components/schemas/Condition'
        action:
          $ref: '#/components/schemas/Action'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    CreatePolicyRequest:
      type: object
      required: [name, action]
      properties:
        name:
          type: string
        description:
          type: string
        priority:
          type: integer
          default: 100
        is_enabled:
          type: boolean
          default: true
        conditions:
          type: array
          items:
            $ref: '#/components/schemas/Condition'
        action:
          $ref: '#/components/schemas/Action'

    # ─── Audit Logs ──────────────────────────────────────────────────────────
    AuditEntry:
      type: object
      properties:
        request_id:
          type: string
        tenant_id:
          type: string
        user_id:
          type: string
        timestamp:
          type: string
          format: date-time
        model_requested:
          type: string
        model_used:
          type: string
        provider:
          type: string
        department:
          type: string
        user_role:
          type: string
        prompt_hash:
          type: string
        sensitivity_level:
          type: string
          enum: [low, medium, high, critical]
        latency_ms:
          type: integer
        pii_entity_count:
          type: integer
        token_input:
          type: integer
        token_output:
          type: integer
        token_total:
          type: integer
        cost_usd:
          type: number
        stream:
          type: boolean
        status:
          type: string
          enum: [ok, error]
        error_type:
          type: string

    AuditResult:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/AuditEntry'
        total:
          type: integer

    # ─── Costs ───────────────────────────────────────────────────────────────
    CostSummary:
      type: object
      properties:
        key:
          type: string
          description: Grouping key (provider name, model name, or department)
        total_tokens:
          type: integer
        total_cost_usd:
          type: number
        request_count:
          type: integer

    CostResult:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/CostSummary'

    # ─── Users ───────────────────────────────────────────────────────────────
    User:
      type: object
      properties:
        id:
          type: string
        tenant_id:
          type: string
        email:
          type: string
          format: email
        first_name:
          type: string
        last_name:
          type: string
        department:
          type: string
        role:
          type: string
          enum: [admin, manager, user, auditor]
        is_active:
          type: boolean
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    CreateUserRequest:
      type: object
      required: [email, role]
      properties:
        email:
          type: string
          format: email
        first_name:
          type: string
        last_name:
          type: string
        department:
          type: string
        role:
          type: string
          enum: [admin, manager, user, auditor]

    # ─── Feature Flags ───────────────────────────────────────────────────────
    FeatureFlag:
      type: object
      properties:
        id:
          type: string
        tenant_id:
          type: string
          description: Empty string means global flag
        name:
          type: string
          example: pii_enabled
        is_enabled:
          type: boolean
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    UpsertFlagRequest:
      type: object
      required: [enabled]
      properties:
        enabled:
          type: boolean

    # ─── Rate Limits ─────────────────────────────────────────────────────────
    RateLimitConfig:
      type: object
      properties:
        tenant_id:
          type: string
        requests_per_min:
          type: integer
        burst_size:
          type: integer
        user_rpm:
          type: integer
        user_burst:
          type: integer
        is_enabled:
          type: boolean

    # ─── Compliance ──────────────────────────────────────────────────────────
    ProcessingEntry:
      type: object
      properties:
        id:
          type: string
        tenant_id:
          type: string
        use_case_name:
          type: string
        legal_basis:
          type: string
          enum: [consent, contract, legal_obligation, vital_interests, public_task, legitimate_interest]
        purpose:
          type: string
        data_categories:
          type: array
          items:
            type: string
        recipients:
          type: array
          items:
            type: string
        processors:
          type: array
          items:
            type: string
        retention_period:
          type: string
          example: 12 months
        security_measures:
          type: string
        controller_name:
          type: string
        risk_level:
          type: string
          enum: [minimal, limited, high, forbidden, ""]
        ai_act_answers:
          type: object
          additionalProperties:
            type: boolean
        is_active:
          type: boolean
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    CreateProcessingEntryRequest:
      type: object
      required: [use_case_name, legal_basis, purpose, retention_period]
      properties:
        use_case_name:
          type: string
        legal_basis:
          type: string
        purpose:
          type: string
        data_categories:
          type: array
          items:
            type: string
        recipients:
          type: array
          items:
            type: string
        processors:
          type: array
          items:
            type: string
        retention_period:
          type: string
        security_measures:
          type: string
        controller_name:
          type: string

    ErasureRecord:
      type: object
      properties:
        erasure_id:
          type: string
        tenant_id:
          type: string
        user_id:
          type: string
        requested_by:
          type: string
        reason:
          type: string
        records_deleted:
          type: integer
        status:
          type: string
          enum: [completed]
        timestamp:
          type: string
          format: date-time

    # ─── Provider Status ─────────────────────────────────────────────────────
    ProviderStatus:
      type: object
      properties:
        provider:
          type: string
        state:
          type: string
          enum: [closed, open, half-open]
        failures:
          type: integer
        last_failure:
          type: string
          format: date-time

    # ─── Errors ──────────────────────────────────────────────────────────────
    APIError:
      type: object
      properties:
        error:
          type: object
          properties:
            type:
              type: string
            message:
              type: string
            code:
              type: string

paths:
  # ─── Health ──────────────────────────────────────────────────────────────────
  /healthz:
    get:
      operationId: healthCheck
      summary: Health check
      description: Returns service health status. No authentication required.
      security: []
      tags: [health]
      responses:
        '200':
          description: Service is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok

  # ─── Chat Completions ─────────────────────────────────────────────────────────
  /v1/chat/completions:
    post:
      operationId: chatCompletions
      summary: Chat completions (OpenAI-compatible)
      description: |
        Drop-in replacement for the OpenAI Chat Completions API. Requests are
        processed through the PII anonymization pipeline, routed to the
        appropriate provider, and logged to the immutable audit trail.

        Set `stream: true` to receive Server-Sent Events (SSE).
      tags: [proxy]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChatRequest'
            examples:
              basic:
                summary: Basic chat request
                value:
                  model: gpt-4o
                  messages:
                    - role: user
                      content: Summarize the Q3 report.
              streaming:
                summary: Streaming request
                value:
                  model: gpt-4o
                  messages:
                    - role: user
                      content: Explain GDPR Article 30.
                  stream: true
      responses:
        '200':
          description: |
            Non-streaming: JSON ChatResponse.
            Streaming: `Content-Type: text/event-stream` with SSE chunks.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChatResponse'
            text/event-stream:
              schema:
                type: string
                description: SSE stream of delta chunks
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'
        '401':
          description: Missing or invalid JWT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'
        '403':
          description: Model not allowed for this role (RBAC)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'
        '502':
          description: Upstream provider error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'

  # ─── PII Analysis ─────────────────────────────────────────────────────────────
  /v1/pii/analyze:
    post:
      operationId: piiAnalyze
      summary: Analyze text for PII entities
      description: |
        Detects and anonymizes PII in the provided text using regex, NER, and
        optional LLM validation (three-layer pipeline). Useful for the Playground.
      tags: [pii]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PIIAnalyzeRequest'
            example:
              text: "Bonjour, je m'appelle Jean Dupont, mon email est jean.dupont@acme.fr"
      responses:
        '200':
          description: PII analysis result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PIIAnalyzeResponse'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'
        '502':
          description: PII service unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'

  # ─── Admin: Routing Policies ──────────────────────────────────────────────────
  /v1/admin/policies:
    get:
      operationId: listPolicies
      summary: List routing policies
      description: Returns all active routing rules for the authenticated tenant.
      tags: [admin-policies]
      responses:
        '200':
          description: List of routing rules
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/RoutingRule'

    post:
      operationId: createPolicy
      summary: Create a routing policy
      tags: [admin-policies]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePolicyRequest'
      responses:
        '201':
          description: Created routing rule
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoutingRule'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'

  /v1/admin/policies/seed/{template}:
    post:
      operationId: seedPolicyTemplate
      summary: Seed a routing policy from a template
      description: |
        Creates a pre-configured routing rule from one of the built-in templates.
        Valid templates: `hr`, `finance`, `engineering`, `catchall`.
      tags: [admin-policies]
      parameters:
        - name: template
          in: path
          required: true
          schema:
            type: string
            enum: [hr, finance, engineering, catchall]
      responses:
        '201':
          description: Seeded routing rule
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoutingRule'
        '400':
          description: Unknown template
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'

  /v1/admin/policies/{id}:
    get:
      operationId: getPolicy
      summary: Get a routing policy by ID
      tags: [admin-policies]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Routing rule
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoutingRule'
        '404':
          description: Policy not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'

    put:
      operationId: updatePolicy
      summary: Update a routing policy
      tags: [admin-policies]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePolicyRequest'
      responses:
        '200':
          description: Updated routing rule
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoutingRule'
        '404':
          description: Policy not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'

    delete:
      operationId: deletePolicy
      summary: Delete a routing policy
      tags: [admin-policies]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: Policy deleted
        '404':
          description: Policy not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'

  # ─── Admin: Audit Logs ────────────────────────────────────────────────────────
  /v1/admin/logs:
    get:
      operationId: getLogs
      summary: Query audit logs
      description: Returns paginated audit log entries for the authenticated tenant.
      tags: [admin-logs]
      parameters:
        - name: provider
          in: query
          schema:
            type: string
        - name: min_sensitivity
          in: query
          schema:
            type: string
            enum: [low, medium, high, critical]
        - name: start
          in: query
          description: RFC3339 timestamp
          schema:
            type: string
            format: date-time
        - name: end
          in: query
          description: RFC3339 timestamp
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Audit log query result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditResult'

  # ─── Admin: Costs ─────────────────────────────────────────────────────────────
  /v1/admin/costs:
    get:
      operationId: getCosts
      summary: Query cost aggregations
      description: Returns token usage and cost grouped by provider, model, or department.
      tags: [admin-logs]
      parameters:
        - name: group_by
          in: query
          schema:
            type: string
            enum: [provider, model, department]
        - name: start
          in: query
          schema:
            type: string
            format: date-time
        - name: end
          in: query
          schema:
            type: string
            format: date-time
      responses:
        '200':
          description: Cost aggregation result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CostResult'

  # ─── Admin: Users ─────────────────────────────────────────────────────────────
  /v1/admin/users:
    get:
      operationId: listUsers
      summary: List users
      tags: [admin-users]
      responses:
        '200':
          description: List of users
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/User'

    post:
      operationId: createUser
      summary: Create a user
      tags: [admin-users]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
      responses:
        '201':
          description: Created user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'

  /v1/admin/users/{id}:
    get:
      operationId: getUser
      summary: Get user by ID
      tags: [admin-users]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: User
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '404':
          description: User not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'

    put:
      operationId: updateUser
      summary: Update a user
      tags: [admin-users]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
      responses:
        '200':
          description: Updated user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'

    delete:
      operationId: deleteUser
      summary: Delete (soft-delete) a user
      tags: [admin-users]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: User deleted

  # ─── Admin: Feature Flags ─────────────────────────────────────────────────────
  /v1/admin/flags:
    get:
      operationId: listFlags
      summary: List feature flags
      description: Returns all flags for the authenticated tenant plus global defaults.
      tags: [admin-flags]
      responses:
        '200':
          description: Feature flags
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/FeatureFlag'

  /v1/admin/flags/{name}:
    put:
      operationId: upsertFlag
      summary: Set a feature flag
      description: |
        Creates or updates a feature flag for the authenticated tenant.
        Built-in flags: `pii_enabled`, `routing_enabled`, `billing_enabled`, `zero_retention`.
      tags: [admin-flags]
      parameters:
        - name: name
          in: path
          required: true
          schema:
            type: string
            example: pii_enabled
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpsertFlagRequest'
      responses:
        '200':
          description: Updated feature flag
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeatureFlag'

    delete:
      operationId: deleteFlag
      summary: Delete a feature flag
      description: Removes a tenant-specific flag, reverting to the global default.
      tags: [admin-flags]
      parameters:
        - name: name
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: Flag deleted
        '404':
          description: Flag not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'

  # ─── Admin: Providers Status ──────────────────────────────────────────────────
  /v1/admin/providers/status:
    get:
      operationId: getProviderStatus
      summary: Get provider circuit breaker status
      description: Returns the circuit breaker state for each configured provider.
      tags: [admin-providers]
      responses:
        '200':
          description: Provider statuses
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ProviderStatus'

  # ─── Admin: Rate Limits ───────────────────────────────────────────────────────
  /v1/admin/rate-limits:
    get:
      operationId: listRateLimits
      summary: List rate limit configurations
      tags: [admin-rate-limits]
      responses:
        '200':
          description: Rate limit configurations
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/RateLimitConfig'

  /v1/admin/rate-limits/{tenant_id}:
    get:
      operationId: getRateLimit
      summary: Get rate limit config for a tenant
      tags: [admin-rate-limits]
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Rate limit config
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitConfig'

    put:
      operationId: upsertRateLimit
      summary: Set rate limit config for a tenant
      tags: [admin-rate-limits]
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RateLimitConfig'
      responses:
        '200':
          description: Updated rate limit config
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitConfig'

    delete:
      operationId: deleteRateLimit
      summary: Delete rate limit config for a tenant
      tags: [admin-rate-limits]
      parameters:
        - name: tenant_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: Rate limit config deleted

  # ─── Admin: Compliance ────────────────────────────────────────────────────────
  /v1/admin/compliance/entries:
    get:
      operationId: listComplianceEntries
      summary: List GDPR Article 30 processing entries
      tags: [admin-compliance]
      responses:
        '200':
          description: Processing entries
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/ProcessingEntry'

    post:
      operationId: createComplianceEntry
      summary: Create a processing entry
      tags: [admin-compliance]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProcessingEntryRequest'
      responses:
        '201':
          description: Created processing entry
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProcessingEntry'

  /v1/admin/compliance/entries/{id}:
    get:
      operationId: getComplianceEntry
      summary: Get a processing entry by ID
      tags: [admin-compliance]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Processing entry
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProcessingEntry'
        '404':
          description: Entry not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'

    put:
      operationId: updateComplianceEntry
      summary: Update a processing entry
      tags: [admin-compliance]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateProcessingEntryRequest'
      responses:
        '200':
          description: Updated processing entry
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProcessingEntry'

    delete:
      operationId: deleteComplianceEntry
      summary: Delete a processing entry
      tags: [admin-compliance]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: Entry deleted

  /v1/admin/compliance/entries/{id}/classify:
    post:
      operationId: classifyComplianceEntry
      summary: Run EU AI Act risk classification
      description: |
        Scores the entry using the five-question questionnaire and sets `risk_level`:
        0 yes → minimal, 1–2 → limited, 3–4 → high, 5 → forbidden.
      tags: [admin-compliance]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ai_act_answers:
                  type: object
                  description: Answers to q1..q5 (true/false)
                  additionalProperties:
                    type: boolean
                  example:
                    q1: true
                    q2: false
                    q3: true
                    q4: false
                    q5: true
      responses:
        '200':
          description: Classified entry with updated risk_level
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProcessingEntry'

  /v1/admin/compliance/gdpr/access/{user_id}:
    get:
      operationId: gdprAccess
      summary: GDPR Art. 15 — right of access
      description: Returns all audit log entries for the given user (data subject access request).
      tags: [admin-compliance]
      parameters:
        - name: user_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: GDPR access report
          content:
            application/json:
              schema:
                type: object
                properties:
                  user_id:
                    type: string
                  generated_at:
                    type: string
                    format: date-time
                  total:
                    type: integer
                  records:
                    type: array
                    items:
                      $ref: '#/components/schemas/AuditEntry'

  /v1/admin/compliance/gdpr/erase/{user_id}:
    delete:
      operationId: gdprErase
      summary: GDPR Art. 17 — right to erasure
      description: |
        Soft-deletes the user and creates an immutable erasure log entry.
        An erasure log record is always created even if `db` is nil (graceful degradation).
      tags: [admin-compliance]
      parameters:
        - name: user_id
          in: path
          required: true
          schema:
            type: string
        - name: reason
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Erasure confirmation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErasureRecord'

tags:
  - name: health
    description: Service health
  - name: proxy
    description: AI proxy (OpenAI-compatible)
  - name: pii
    description: PII detection and anonymization
  - name: admin-policies
    description: Routing policy management
  - name: admin-logs
    description: Audit logs and cost reporting
  - name: admin-users
    description: User management
  - name: admin-flags
    description: Feature flag management
  - name: admin-providers
    description: Provider circuit breaker status
  - name: admin-rate-limits
    description: Rate limit configuration
  - name: admin-compliance
    description: GDPR / EU AI Act compliance registry
