Appearance
Control Plane API Reference
The Control Plane API is the backend orchestration layer for the Gatez operator and developer portals. It manages tenant lifecycle, API key provisioning, rate limit configuration, analytics, health aggregation, notifications, audit trails, branding, and platform settings.
Base URL: http://localhost:4001
All endpoints require authentication. CORS is configured to allow requests from the operator portal (localhost:3003) and developer portal (localhost:3004), configurable via the ALLOWED_ORIGINS environment variable.
Health
GET /health
Health check with dependency status.
Response: 200 OK
json
{
"status": "healthy",
"service": "control-plane-api",
"version": "0.1.0",
"checks": {
"redis": "connected"
}
}If Redis is unreachable, status returns "degraded" and redis returns "unreachable".
curl Example:
bash
curl http://localhost:4001/healthGET /metrics
Prometheus-format metrics endpoint.
Response: 200 OK (text/plain, Prometheus exposition format)
curl Example:
bash
curl http://localhost:4001/metricsGET /api/health/services
Aggregate health status from all platform layers (APISIX, Redis, AI Gateway, Agent Gateway, ClickHouse, Keycloak).
Response: 200 OK
json
[
{"layer": "L1", "service": "APISIX", "status": "healthy", "latency_ms": 12, "url": "http://gw-apisix:9180"},
{"layer": "L1", "service": "Redis", "status": "healthy", "latency_ms": 1, "url": "redis://gw-redis:6379"},
{"layer": "L2", "service": "AI Gateway", "status": "healthy", "latency_ms": 5, "url": "http://ai-gateway:4000"},
{"layer": "L3", "service": "Agent Gateway", "status": "healthy", "latency_ms": 8, "url": "http://agent-gateway:5001"},
{"layer": "L1", "service": "ClickHouse", "status": "healthy", "latency_ms": 15, "url": "http://gw-clickhouse:8123"},
{"layer": "L1", "service": "Keycloak", "status": "healthy", "latency_ms": 45, "url": "http://gw-keycloak:8081"}
]Each entry includes:
| Field | Type | Description |
|---|---|---|
layer | string | Platform layer (L1, L2, L3) |
service | string | Service name |
status | string | healthy, degraded, or down |
latency_ms | integer | Probe latency in milliseconds |
url | string | Internal service URL |
curl Example:
bash
curl http://localhost:4001/api/health/services \
-H "Authorization: Bearer $TOKEN"GET /api/health/alerts
List active alerts from Prometheus Alertmanager.
Response: 200 OK
json
[]curl Example:
bash
curl http://localhost:4001/api/health/alerts \
-H "Authorization: Bearer $TOKEN"Tenants
GET /api/tenants
List all tenants with usage statistics.
Response: 200 OK
json
[
{
"id": "tenant-alpha",
"name": "Alpha Corp",
"plan": "pro",
"status": "active",
"rate_limit_max": 2500,
"rate_limit_usage": 0,
"token_budget": 2000000,
"token_budget_used": 150000,
"request_count_24h": 0,
"created_at": "2026-03-21"
}
]curl Example:
bash
curl http://localhost:4001/api/tenants \
-H "Authorization: Bearer $TOKEN"POST /api/tenants
Create a new tenant. Atomically provisions across all layers: Redis metadata, rate limit bucket, token budget, APISIX consumer group.
Request Body:
json
{
"name": "Beta Inc",
"plan": "pro",
"admin_email": "admin@beta.com",
"realm_name": "beta",
"claim_path": "tenant_id"
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Tenant display name |
plan | string | Yes | Plan tier: free, pro, or enterprise |
id | string | No | Custom tenant ID (auto-generated if omitted) |
admin_email | string | No | Admin contact email |
realm_name | string | No | Keycloak realm name |
claim_path | string | No | JWT claim path for tenant extraction |
Plan defaults:
| Plan | Rate Limit (req/min) | Token Budget |
|---|---|---|
free | 100 | 50,000 |
pro | 2,500 | 2,000,000 |
enterprise | 25,000 | 10,000,000 |
Response: 201 Created
json
{
"id": "tenant-beta",
"name": "Beta Inc",
"plan": "pro",
"status": "active",
"rate_limit_max": 2500,
"rate_limit_usage": 0,
"token_budget": 2000000,
"token_budget_used": 0,
"request_count_24h": 0,
"created_at": "2026-03-25"
}Error Response: 409 Conflict
json
{"error": "Tenant already exists"}curl Example:
bash
curl -X POST http://localhost:4001/api/tenants \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Beta Inc", "plan": "pro", "admin_email": "admin@beta.com"}'GET /api/tenants/:id
Get a specific tenant with current usage.
Path Parameters:
| Parameter | Description |
|---|---|
id | Tenant identifier |
Response: 200 OK
json
{
"id": "tenant-alpha",
"name": "Alpha Corp",
"plan": "pro",
"status": "active",
"rate_limit_max": 2500,
"rate_limit_usage": 0,
"token_budget": 2000000,
"token_budget_used": 150000,
"request_count_24h": 0,
"created_at": "2026-03-21"
}Error Response: 404 Not Found
json
{"error": "Tenant not found"}curl Example:
bash
curl http://localhost:4001/api/tenants/tenant-alpha \
-H "Authorization: Bearer $TOKEN"PATCH /api/tenants/:id
Update tenant properties.
Path Parameters:
| Parameter | Description |
|---|---|
id | Tenant identifier |
Request Body:
json
{
"plan": "enterprise",
"status": "active",
"rate_limit_max": 25000,
"token_budget": 10000000
}All fields are optional. Only provided fields are updated.
| Field | Type | Description |
|---|---|---|
plan | string | Plan tier |
status | string | Tenant status (active, suspended) |
rate_limit_max | integer | Max requests per minute |
token_budget | integer | Token budget (resets remaining to this value) |
Response: 200 OK
json
{"status": "updated"}curl Example:
bash
curl -X PATCH http://localhost:4001/api/tenants/tenant-alpha \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"plan": "enterprise", "rate_limit_max": 25000}'DELETE /api/tenants/:id
Delete a tenant and all associated resources (Redis metadata, rate limit bucket, token budget).
Path Parameters:
| Parameter | Description |
|---|---|
id | Tenant identifier |
Response: 200 OK
json
{"status": "deleted"}curl Example:
bash
curl -X DELETE http://localhost:4001/api/tenants/tenant-alpha \
-H "Authorization: Bearer $TOKEN"Keys
GET /api/keys
List all API keys (masked) across all tenants.
Response: 200 OK
json
[
{
"id": "k-abc12345",
"prefix": "****ab12",
"name": "my-app-key",
"tenant_id": "tenant-alpha",
"routes": ["/v1/chat/completions"],
"status": "active",
"created_at": "2026-03-22",
"expires_at": null,
"request_count": 0
}
]curl Example:
bash
curl http://localhost:4001/api/keys \
-H "Authorization: Bearer $TOKEN"POST /api/keys/request
Submit an API key request (developer workflow).
Request Body:
json
{
"name": "my-app-key",
"tenant_id": "tenant-alpha",
"routes": ["/v1/chat/completions", "/v1/models"],
"reason": "Need API access for my application",
"email": "dev@alpha.com"
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Key name (becomes APISIX consumer name) |
tenant_id | string | Yes | Tenant identifier |
routes | array | Yes | Requested route access |
reason | string | Yes | Justification for the key |
email | string | Yes | Requester email |
Response: 201 Created
json
{
"id": "kr-abc12345",
"email": "dev@alpha.com",
"tenant_id": "tenant-alpha",
"name": "my-app-key",
"routes": ["/v1/chat/completions", "/v1/models"],
"reason": "Need API access for my application",
"requested_at": "2026-03-25T10:00:00Z",
"status": "pending"
}curl Example:
bash
curl -X POST http://localhost:4001/api/keys/request \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "my-app-key",
"tenant_id": "tenant-alpha",
"routes": ["/v1/chat/completions"],
"reason": "API access for my app",
"email": "dev@alpha.com"
}'GET /api/keys/requests
List pending API key requests.
Response: 200 OK
json
[
{
"id": "kr-abc12345",
"email": "dev@alpha.com",
"tenant_id": "tenant-alpha",
"name": "my-app-key",
"routes": ["/v1/chat/completions"],
"reason": "API access for my app",
"requested_at": "2026-03-25T10:00:00Z",
"status": "pending"
}
]curl Example:
bash
curl http://localhost:4001/api/keys/requests \
-H "Authorization: Bearer $TOKEN"POST /api/keys/requests/:id/approve
Approve a key request. Creates an APISIX consumer with key-auth plugin and returns the full API key (shown only once).
Path Parameters:
| Parameter | Description |
|---|---|
id | Key request identifier |
Response: 200 OK
json
{
"status": "approved",
"key_id": "k-abc12345",
"full_key": "gtz_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"name": "my-app-key"
}DANGER
The full_key is returned only once upon approval. Store it securely. It cannot be retrieved again.
curl Example:
bash
curl -X POST http://localhost:4001/api/keys/requests/kr-abc12345/approve \
-H "Authorization: Bearer $TOKEN"POST /api/keys/requests/:id/deny
Deny a key request.
Path Parameters:
| Parameter | Description |
|---|---|
id | Key request identifier |
Response: 200 OK
json
{"status": "denied"}curl Example:
bash
curl -X POST http://localhost:4001/api/keys/requests/kr-abc12345/deny \
-H "Authorization: Bearer $TOKEN"DELETE /api/keys/:id
Revoke an API key. Removes the APISIX consumer and marks the key as revoked.
Path Parameters:
| Parameter | Description |
|---|---|
id | API key identifier |
Response: 200 OK
json
{"status": "revoked"}curl Example:
bash
curl -X DELETE http://localhost:4001/api/keys/k-abc12345 \
-H "Authorization: Bearer $TOKEN"Rate Limits
GET /api/rate-limits/:tenant_id
Get the rate limit hierarchy for a tenant (global default, tenant override, route overrides).
Path Parameters:
| Parameter | Description |
|---|---|
tenant_id | Tenant identifier |
Response: 200 OK
json
{
"tenant_id": "tenant-alpha",
"global_default": {
"limit": 5000,
"window": "60s",
"current_traffic": 0
},
"tenant_override": {
"limit": 2500,
"window": "60s",
"current_traffic": 0
},
"route_overrides": []
}If no tenant override is set, tenant_override is null.
curl Example:
bash
curl http://localhost:4001/api/rate-limits/tenant-alpha \
-H "Authorization: Bearer $TOKEN"PUT /api/rate-limits/:tenant_id
Update the tenant-level rate limit override.
Path Parameters:
| Parameter | Description |
|---|---|
tenant_id | Tenant identifier |
Request Body:
json
{
"limit": 5000,
"window": "60s"
}| Field | Type | Required | Description |
|---|---|---|---|
limit | integer | Yes | Requests per window |
window | string | No | Time window (default: 60s) |
Response: 200 OK
json
{"status": "updated", "limit": 5000}curl Example:
bash
curl -X PUT http://localhost:4001/api/rate-limits/tenant-alpha \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"limit": 5000}'PUT /api/rate-limits/:tenant_id/routes/:route_id
Update a route-level rate limit override for a tenant.
Path Parameters:
| Parameter | Description |
|---|---|
tenant_id | Tenant identifier |
route_id | APISIX route identifier |
Request Body:
json
{
"limit": 1000,
"window": "60s"
}Response: 200 OK
json
{"status": "updated", "route_id": "route-123", "limit": 1000}curl Example:
bash
curl -X PUT http://localhost:4001/api/rate-limits/tenant-alpha/routes/route-123 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"limit": 1000}'Analytics
All analytics endpoints query ClickHouse and accept the following query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
tenant_id | string | (all tenants) | Filter by tenant |
range | string | 24h | Time range: 1h, 24h, 7d, 30d |
GET /api/analytics/traffic
L1 API Gateway traffic analytics (requests, errors, latency).
Response: 200 OK
json
{
"data": [
{
"hour": "2026-03-25 10:00:00",
"requests": 1520,
"errors": 12,
"avg_latency_ms": 8.5,
"p99_latency_ms": 42.3
}
]
}curl Example:
bash
curl "http://localhost:4001/api/analytics/traffic?tenant_id=tenant-alpha&range=24h" \
-H "Authorization: Bearer $TOKEN"GET /api/analytics/llm
L2 AI Gateway LLM usage analytics (tokens, models, cache hits).
Response: 200 OK
json
{
"data": [
{
"hour": "2026-03-25 10:00:00",
"model": "gpt-4o",
"provider": "openai",
"prompt_tokens": 45000,
"completion_tokens": 12000,
"total_tokens": 57000,
"avg_latency_ms": 350.2,
"cache_hits": 42,
"total_requests": 120
}
]
}curl Example:
bash
curl "http://localhost:4001/api/analytics/llm?tenant_id=tenant-alpha&range=7d" \
-H "Authorization: Bearer $TOKEN"GET /api/analytics/agents
L3 Agent Gateway analytics (tool calls, actions, errors).
Response: 200 OK
json
{
"data": [
{
"hour": "2026-03-25 10:00:00",
"action": "tool_call",
"tool_name": "read_file",
"total": 85,
"errors": 2
}
]
}curl Example:
bash
curl "http://localhost:4001/api/analytics/agents?range=24h" \
-H "Authorization: Bearer $TOKEN"Audit
GET /api/audit
Query the audit trail from ClickHouse. Supports filtering and CSV export.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
tenant_id | string | (all) | Filter by tenant |
action | string | (all) | Filter by action (partial match) |
from | string | (none) | Start timestamp (ISO 8601) |
to | string | (none) | End timestamp (ISO 8601) |
limit | integer | 100 | Max rows (capped at 1000) |
format | string | json | Response format: json or csv |
Response (JSON): 200 OK
json
{
"data": [
{
"timestamp": "2026-03-25T10:05:00Z",
"tenant_id": "tenant-alpha",
"session_id": "sess-xyz789",
"agent_id": "agent-orchestrator",
"tool_name": "read_file",
"action": "tool_call",
"status": "success",
"trace_id": "abc123"
}
],
"rows": 1
}Response (CSV): 200 OK with Content-Type: text/csv and Content-Disposition: attachment; filename=audit_log.csv
curl Example (JSON):
bash
curl "http://localhost:4001/api/audit?tenant_id=tenant-alpha&action=tool_call&limit=50" \
-H "Authorization: Bearer $TOKEN"curl Example (CSV):
bash
curl "http://localhost:4001/api/audit?tenant_id=tenant-alpha&format=csv" \
-H "Authorization: Bearer $TOKEN" \
-o audit_log.csvExport
GET /api/export/:table
Generic CSV export for ClickHouse tables.
Path Parameters:
| Parameter | Description |
|---|---|
table | Table name (allowed: request_log, ai_request_log, agent_audit_log) |
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
tenant_id | string | (all) | Filter by tenant |
from | string | (none) | Start timestamp |
to | string | (none) | End timestamp |
limit | integer | 1000 | Max rows (capped at 10000) |
Columns by table:
| Table | Columns |
|---|---|
request_log | timestamp, tenant_id, route_id, method, status_code, latency_ms, trace_id |
ai_request_log | timestamp, tenant_id, model, tokens_prompt, tokens_completion, cache_hit, pii_detected, latency_ms, trace_id |
agent_audit_log | timestamp, tenant_id, session_id, agent_id, tool_name, action, status, trace_id |
Response: 200 OK with Content-Type: text/csv and Content-Disposition: attachment; filename={table}.csv
Error Response (invalid table):
json
{"error": "Invalid table name"}curl Example:
bash
curl "http://localhost:4001/api/export/ai_request_log?tenant_id=tenant-alpha&limit=500" \
-H "Authorization: Bearer $TOKEN" \
-o ai_request_log.csvNotifications
GET /api/notifications
List notifications for a tenant.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
tenant_id | string | all | Tenant identifier |
unread_only | boolean | false | Filter to unread only |
Response: 200 OK
json
{
"notifications": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": "tenant-alpha",
"notification_type": "key_approved",
"title": "API Key Approved",
"message": "Your key request 'my-app-key' has been approved.",
"read": false,
"created_at": "2026-03-25T10:00:00Z"
}
],
"total": 1,
"tenant_id": "tenant-alpha"
}Notification types: key_approved, key_denied, budget_warning, hitl_pending, session_errored, health_degraded.
curl Example:
bash
curl "http://localhost:4001/api/notifications?tenant_id=tenant-alpha&unread_only=true" \
-H "Authorization: Bearer $TOKEN"POST /api/notifications
Create a notification (internal use).
Request Body:
json
{
"tenant_id": "tenant-alpha",
"notification_type": "budget_warning",
"title": "Token Budget Low",
"message": "Your token budget is below 10%. Current remaining: 5,000 tokens."
}| Field | Type | Required | Description |
|---|---|---|---|
tenant_id | string | Yes | Target tenant |
notification_type | string | Yes | Notification category |
title | string | Yes | Short title |
message | string | Yes | Notification body |
Response: 201 Created
json
{"id": "550e8400-e29b-41d4-a716-446655440000", "status": "created"}curl Example:
bash
curl -X POST http://localhost:4001/api/notifications \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"tenant_id": "tenant-alpha",
"notification_type": "budget_warning",
"title": "Token Budget Low",
"message": "Your token budget is below 10%."
}'POST /api/notifications/:id/read
Mark a notification as read.
Path Parameters:
| Parameter | Description |
|---|---|
id | Notification identifier |
Response: 200 OK
json
{"id": "550e8400-e29b-41d4-a716-446655440000", "read": true}curl Example:
bash
curl -X POST http://localhost:4001/api/notifications/550e8400-e29b-41d4-a716-446655440000/read \
-H "Authorization: Bearer $TOKEN"GET /api/notifications/preferences
Get notification preferences for a tenant.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
tenant_id | string | default | Tenant identifier |
Response: 200 OK
json
{
"tenant_id": "tenant-alpha",
"webhook_url": null,
"slack_url": null,
"email": null,
"enabled_types": ["key_approved", "key_denied", "budget_warning", "hitl_pending"]
}curl Example:
bash
curl "http://localhost:4001/api/notifications/preferences?tenant_id=tenant-alpha" \
-H "Authorization: Bearer $TOKEN"PUT /api/notifications/preferences
Update notification preferences for a tenant.
Request Body:
json
{
"tenant_id": "tenant-alpha",
"webhook_url": "https://hooks.example.com/notify",
"slack_url": "https://hooks.slack.com/services/T00/B00/xxxx",
"email": "alerts@alpha.com",
"enabled_types": ["key_approved", "budget_warning", "hitl_pending", "health_degraded"]
}Response: 200 OK
json
{"status": "updated", "tenant_id": "tenant-alpha"}curl Example:
bash
curl -X PUT http://localhost:4001/api/notifications/preferences \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"tenant_id": "tenant-alpha",
"webhook_url": "https://hooks.example.com/notify",
"enabled_types": ["key_approved", "budget_warning"]
}'Branding
GET /api/tenants/:id/branding
Get tenant-specific portal branding.
Path Parameters:
| Parameter | Description |
|---|---|
id | Tenant identifier |
Response: 200 OK
json
{
"tenant_id": "tenant-alpha",
"logo_base64": null,
"portal_title": "Gatez Developer Portal",
"primary_color": null,
"updated_at": null
}curl Example:
bash
curl http://localhost:4001/api/tenants/tenant-alpha/branding \
-H "Authorization: Bearer $TOKEN"POST /api/tenants/:id/branding
Update tenant portal branding. Logo must be under 100KB (base64-encoded).
Path Parameters:
| Parameter | Description |
|---|---|
id | Tenant identifier |
Request Body:
json
{
"portal_title": "Alpha Dev Portal",
"primary_color": "#2563eb",
"logo_base64": "iVBORw0KGgoAAAANSUhEUg..."
}| Field | Type | Required | Description |
|---|---|---|---|
logo_base64 | string | No | Base64-encoded logo image (max 100KB) |
portal_title | string | No | Custom portal title |
primary_color | string | No | Primary color hex code |
Response: 200 OK
json
{
"tenant_id": "tenant-alpha",
"logo_base64": "iVBORw0KGgoAAAANSUhEUg...",
"portal_title": "Alpha Dev Portal",
"primary_color": "#2563eb",
"updated_at": "2026-03-25T12:00:00Z"
}Error Response: 400 Bad Request
json
{"error": "Logo exceeds 100KB limit"}curl Example:
bash
curl -X POST http://localhost:4001/api/tenants/tenant-alpha/branding \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"portal_title": "Alpha Dev Portal", "primary_color": "#2563eb"}'Settings
GET /api/settings
Get platform-wide settings.
Response: 200 OK
json
{
"platform_name": "Gatez Gateway Platform",
"default_rate_limit": 100,
"default_token_budget": 100000,
"data_retention": {
"request_log_ttl_days": 90,
"ai_request_log_ttl_days": 365,
"audit_log_ttl": "forever"
},
"notification_config": {
"slack_webhook_url": null,
"email_smtp": null,
"pagerduty_key": null
}
}curl Example:
bash
curl http://localhost:4001/api/settings \
-H "Authorization: Bearer $TOKEN"PUT /api/settings
Update platform-wide settings.
Request Body:
json
{
"platform_name": "Gatez Gateway Platform",
"default_rate_limit": 200,
"default_token_budget": 200000,
"data_retention": {
"request_log_ttl_days": 90,
"ai_request_log_ttl_days": 365,
"audit_log_ttl": "forever"
},
"notification_config": {
"slack_webhook_url": "https://hooks.slack.com/services/T00/B00/xxxx",
"email_smtp": null,
"pagerduty_key": null
}
}Response: 200 OK
json
{
"status": "updated",
"settings": { ... }
}curl Example:
bash
curl -X PUT http://localhost:4001/api/settings \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"platform_name": "Gatez", "default_rate_limit": 200, "default_token_budget": 200000}'API Lifecycle
PATCH /api/routes/:id/lifecycle
Update the lifecycle state of an APISIX route. Updates the route labels in APISIX and logs the change to the audit trail.
Path Parameters:
| Parameter | Description |
|---|---|
id | APISIX route identifier |
Request Body:
json
{
"lifecycle": "deprecated"
}| Field | Type | Required | Values |
|---|---|---|---|
lifecycle | string | Yes | draft, published, deprecated, retired |
Response: 200 OK
json
{"status": "updated", "route_id": "route-123", "lifecycle": "deprecated"}Error Response: 400 Bad Request
json
{"error": "Invalid lifecycle state"}curl Example:
bash
curl -X PATCH http://localhost:4001/api/routes/route-123/lifecycle \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"lifecycle": "deprecated"}'Endpoint Summary
| Method | Path | Description |
|---|---|---|
| GET | /health | Health check |
| GET | /metrics | Prometheus metrics |
| GET | /api/health/services | Aggregate service health |
| GET | /api/health/alerts | Active alerts |
| GET | /api/tenants | List tenants |
| POST | /api/tenants | Create tenant |
| GET | /api/tenants/:id | Get tenant |
| PATCH | /api/tenants/:id | Update tenant |
| DELETE | /api/tenants/:id | Delete tenant |
| GET | /api/keys | List API keys |
| POST | /api/keys/request | Request API key |
| GET | /api/keys/requests | List pending requests |
| POST | /api/keys/requests/:id/approve | Approve key request |
| POST | /api/keys/requests/:id/deny | Deny key request |
| DELETE | /api/keys/:id | Revoke API key |
| GET | /api/rate-limits/:tenant_id | Get rate limit hierarchy |
| PUT | /api/rate-limits/:tenant_id | Update tenant rate limit |
| PUT | /api/rate-limits/:tenant_id/routes/:route_id | Update route rate limit |
| GET | /api/analytics/traffic | Traffic analytics |
| GET | /api/analytics/llm | LLM usage analytics |
| GET | /api/analytics/agents | Agent analytics |
| GET | /api/audit | Query audit trail |
| GET | /api/export/:table | CSV export |
| GET | /api/notifications | List notifications |
| POST | /api/notifications | Create notification |
| POST | /api/notifications/:id/read | Mark as read |
| GET | /api/notifications/preferences | Get notification preferences |
| PUT | /api/notifications/preferences | Update notification preferences |
| GET | /api/tenants/:id/branding | Get branding |
| POST | /api/tenants/:id/branding | Update branding |
| GET | /api/settings | Get platform settings |
| PUT | /api/settings | Update platform settings |
| PATCH | /api/routes/:id/lifecycle | Update route lifecycle |
| PATCH | /api/routes/:id/canary | Set canary traffic split (weight + upstream) |
| GET | /api/license | Get current license tier, validity, expiry |
| GET | /api/tenants/:id/ip-allowlist | Get tenant IP allowlist CIDRs |
| PUT | /api/tenants/:id/ip-allowlist | Set tenant IP allowlist CIDRs |
| GET | /api/webhooks | List webhooks (optionally ?tenant_id=) |
| POST | /api/webhooks | Register webhook (URL + event types) |
| DELETE | /api/webhooks/:id | Delete webhook |