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