Membrain API Reference¶
Complete reference for every HTTP endpoint exposed by the Membrain AI Safety Gateway.
Base URL: http://localhost:8000 (default)
Table of Contents¶
- Authentication
- Rate Limit Headers
- Error Response Format
- Health
- Chat Completions
- Anthropic Proxy
- Knowledge
- Audit
- Admin
- Reports
- Shadow AI
- Dashboard
- Metrics
Authentication¶
Membrain uses API keys for authentication. When a database is configured, API keys are validated against the api_keys table. When no database is configured, authentication is disabled and all requests are allowed.
API Key Format¶
Keys follow the format ck_live_<32 hex characters>, for example:
Providing the Key¶
Membrain accepts API keys via two headers (checked in this order):
| Header | Format | Example |
|---|---|---|
X-Membrain-API-Key |
Raw key | X-Membrain-API-Key: ck_live_a1b2... |
Authorization |
Bearer token | Authorization: Bearer ck_live_a1b2... |
Authentication Levels¶
| Level | Description |
|---|---|
| None | No authentication required. Endpoint is publicly accessible. |
| Optional | API key is accepted but not required. Invalid keys are rejected with 401. |
| Required (Admin) | Valid API key is mandatory. Returns 401 if missing, 403 if inactive/expired. |
Rate Limit Headers¶
When rate limiting is active, successful responses from /v1/chat/completions include these headers:
| Header | Type | Description |
|---|---|---|
X-RateLimit-Limit |
integer | Maximum requests allowed in the current window |
X-RateLimit-Remaining |
integer | Requests remaining in the current window |
X-RateLimit-Reset |
integer | Unix timestamp when the rate limit window resets |
Error Response Format¶
Standard Error (Chat/Dashboard/Admin endpoints)¶
Common HTTP status codes:
| Code | Meaning |
|---|---|
400 |
Bad request (invalid input, malformed UUID, etc.) |
401 |
Authentication required or invalid API key |
402 |
Budget exceeded |
403 |
API key inactive or expired |
404 |
Resource not found |
409 |
Conflict (duplicate resource) |
429 |
Rate limit exceeded (includes Retry-After header) |
502 |
All upstream providers failed |
503 |
Service unavailable (database or knowledge system not configured) |
504 |
Upstream request timed out |
Anthropic-Style Error (Anthropic proxy endpoints)¶
Error types: authentication_error, invalid_request_error, api_error, timeout_error.
1. Health¶
GET /health¶
Simple health check to verify the service is running.
Authentication: None
Response Body:
| Field | Type | Description |
|---|---|---|
status |
string | Always "ok" |
Example:
2. Chat Completions¶
POST /v1/chat/completions¶
OpenAI-compatible chat completions endpoint. Runs the full middleware pipeline (PII sanitization, rate limiting, budget enforcement, caching, knowledge injection), routes to the appropriate provider with fallback, and logs an audit entry.
Authentication: None (pipeline may enforce per-key limits when X-Membrain-API-Key is provided)
Request Headers (optional routing controls):
| Header | Type | Default | Description |
|---|---|---|---|
X-Membrain-Tier |
string | "balanced" |
Routing tier: "fast", "balanced", "quality" |
X-Membrain-Private |
string | "" |
Set to "true" or "1" to restrict to private/local models |
X-Membrain-Max-Cost |
string | null |
Maximum cost per 1K tokens (as a float string) |
X-Membrain-User-Id |
string | null |
User identifier for audit and rate limiting |
X-Membrain-Project |
string | null |
Project identifier for audit and rate limiting |
Request Body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
model |
string | Yes | Model name (e.g., "gpt-4", "claude-sonnet-4-20250514") |
|
messages |
array | Yes | List of chat messages | |
messages[].role |
string | Yes | Message role: "system", "user", "assistant" |
|
messages[].content |
string | No | null |
Message content text |
temperature |
float | No | null |
Sampling temperature (0.0 to 2.0) |
max_tokens |
integer | No | null |
Maximum tokens to generate |
stream |
boolean | No | false |
Enable SSE streaming |
Response Body (non-streaming):
| Field | Type | Description |
|---|---|---|
id |
string | Unique completion ID |
object |
string | Always "chat.completion" |
created |
integer | Unix timestamp of creation |
model |
string | Model that generated the response |
choices |
array | List of completion choices |
choices[].index |
integer | Choice index |
choices[].message.role |
string | Always "assistant" |
choices[].message.content |
string | Generated text |
choices[].finish_reason |
string | Stop reason: "stop", "length", etc. |
usage.prompt_tokens |
integer | Input tokens consumed |
usage.completion_tokens |
integer | Output tokens generated |
usage.total_tokens |
integer | Total tokens used |
Response (streaming): Server-Sent Events (text/event-stream) with OpenAI-compatible data: {...} chunks.
Example:
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "X-Membrain-Tier: quality" \
-d '{
"model": "gpt-4",
"messages": [
{"role": "user", "content": "Hello, world!"}
],
"temperature": 0.7,
"max_tokens": 100
}'
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1709856000,
"model": "gpt-4",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 9,
"total_tokens": 21
}
}
Error Responses:
| Code | Condition |
|---|---|
402 |
Budget exceeded |
429 |
Rate limit exceeded (includes Retry-After header) |
502 |
All providers failed |
3. Anthropic Proxy¶
POST /v1/messages¶
Proxies requests to the upstream Anthropic Messages API. Sanitizes PII in user messages and system prompts before forwarding, then restores PII placeholders in the response. Supports both streaming and non-streaming modes. Preserves byte-exact representations of signed thinking blocks.
Authentication: Anthropic API key via X-API-Key header, Authorization header, or server-configured ANTHROPIC_API_KEY.
Forwarded Headers:
| Header | Description |
|---|---|
x-api-key |
Anthropic API key |
authorization |
Bearer token authentication |
anthropic-version |
API version string |
anthropic-beta |
Beta feature flags |
content-type |
Request content type |
Request Body: Standard Anthropic Messages API request body. Key fields:
| Field | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Model name (e.g., "claude-sonnet-4-20250514") |
messages |
array | Yes | Conversation messages |
max_tokens |
integer | Yes | Maximum tokens to generate |
stream |
boolean | No | Enable SSE streaming |
system |
string | No | System prompt |
temperature |
float | No | Sampling temperature |
Response Body: Standard Anthropic Messages API response, passed through with PII restored.
Response Headers (forwarded from upstream):
| Header | Description |
|---|---|
request-id |
Anthropic request ID |
x-ratelimit-* |
Upstream Anthropic rate limit headers |
Response (streaming): Server-Sent Events (text/event-stream) proxied from the Anthropic API with PII restored.
Example:
curl -X POST http://localhost:8000/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: sk-ant-..." \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-20250514",
"max_tokens": 256,
"messages": [
{"role": "user", "content": "What is the capital of France?"}
]
}'
{
"id": "msg_01abc...",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "The capital of France is Paris."
}
],
"model": "claude-sonnet-4-20250514",
"stop_reason": "end_turn",
"usage": {
"input_tokens": 15,
"output_tokens": 10
}
}
Error Responses:
| Code | Condition |
|---|---|
400 |
Invalid JSON in request body |
401 |
Missing API key |
502 |
Failed to connect to upstream Anthropic API |
504 |
Upstream request timed out |
POST /v1/messages/count_tokens¶
Transparent passthrough to the Anthropic token counting endpoint. No PII sanitization is applied.
Authentication: Same as /v1/messages (Anthropic API key).
Request Body: Standard Anthropic token counting request body.
Response Body: Upstream response passed through verbatim.
Response Headers (forwarded from upstream):
| Header | Description |
|---|---|
request-id |
Anthropic request ID |
x-ratelimit-* |
Upstream Anthropic rate limit headers |
Example:
curl -X POST http://localhost:8000/v1/messages/count_tokens \
-H "Content-Type: application/json" \
-H "x-api-key: sk-ant-..." \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-20250514",
"messages": [
{"role": "user", "content": "Hello, world!"}
]
}'
Error Responses:
| Code | Condition |
|---|---|
401 |
Missing API key |
502 |
Failed to connect to upstream |
504 |
Upstream request timed out |
4. Knowledge¶
All knowledge endpoints are prefixed with /v1/knowledge.
POST /v1/knowledge¶
Add a single document or snippet to the knowledge store.
Authentication: None
Request Body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
content |
string | Yes | Document text (1 to 100,000 characters) | |
user_id |
string | No | null |
User who contributed this knowledge |
project |
string | No | null |
Project this knowledge belongs to |
Response Body (201 Created):
| Field | Type | Description |
|---|---|---|
id |
string | UUID of the created entry |
content |
string | Stored content |
user_id |
string or null | User identifier |
project |
string or null | Project identifier |
source |
string or null | Source tag (e.g., "api_manual") |
trust_level |
float or null | Trust score of the entry |
created_at |
string | ISO 8601 timestamp |
Example:
curl -X POST http://localhost:8000/v1/knowledge \
-H "Content-Type: application/json" \
-d '{
"content": "Membrain supports PII detection for 25+ patterns including emails, SSNs, and credit cards.",
"project": "docs"
}'
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"content": "Membrain supports PII detection for 25+ patterns including emails, SSNs, and credit cards.",
"user_id": null,
"project": "docs",
"source": "api_manual",
"trust_level": null,
"created_at": "2026-03-08T12:00:00+00:00"
}
GET /v1/knowledge/search¶
Semantic search over the knowledge store using vector similarity (pgvector).
Authentication: None
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
q |
string | Yes | Search query (min 1 character) | |
limit |
integer | No | 5 |
Max results to return (1-50) |
project |
string | No | null |
Filter by project |
topic |
string | No | null |
Filter by topic |
Response Body:
| Field | Type | Description |
|---|---|---|
query |
string | The original search query |
results |
array | List of matching knowledge entries |
results[].id |
string | Entry UUID |
results[].content |
string | Entry content |
results[].user_id |
string or null | User identifier |
results[].project |
string or null | Project identifier |
results[].source |
string or null | Source tag |
results[].trust_level |
float or null | Trust score |
results[].is_stale |
boolean | Whether the entry is marked stale |
results[].created_at |
string | ISO 8601 timestamp |
Example:
{
"query": "PII detection",
"results": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"content": "Membrain supports PII detection for 25+ patterns...",
"user_id": null,
"project": "docs",
"source": "api_manual",
"trust_level": 0.95,
"is_stale": false,
"created_at": "2026-03-08T12:00:00+00:00"
}
]
}
DELETE /v1/knowledge/{entry_id}¶
Remove a knowledge entry by its UUID.
Authentication: None
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
entry_id |
string (UUID) | The UUID of the knowledge entry to delete |
Response: 204 No Content on success.
Example:
Error Responses:
| Code | Condition |
|---|---|
400 |
Invalid UUID format |
404 |
Knowledge entry not found |
503 |
Knowledge system not available |
POST /v1/knowledge/ingest¶
Bulk ingest multiple documents into the knowledge store.
Authentication: None
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
documents |
array | Yes | List of documents to ingest |
documents[].content |
string | Yes | Document text (1 to 100,000 chars) |
documents[].user_id |
string | No | User identifier |
documents[].project |
string | No | Project identifier |
Response Body:
| Field | Type | Description |
|---|---|---|
ingested |
integer | Number of documents successfully ingested |
failed |
integer | Number of documents that failed to ingest |
ids |
array of strings | UUIDs of successfully ingested entries |
Example:
curl -X POST http://localhost:8000/v1/knowledge/ingest \
-H "Content-Type: application/json" \
-d '{
"documents": [
{"content": "First document content", "project": "docs"},
{"content": "Second document content", "project": "docs"}
]
}'
{
"ingested": 2,
"failed": 0,
"ids": [
"a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"b2c3d4e5-f6a1-8901-bcde-f12345678901"
]
}
GET /v1/knowledge/topics¶
Return all distinct topics across knowledge entries.
Authentication: None
Response Body:
| Field | Type | Description |
|---|---|---|
topics |
array of strings | Sorted list of all unique topics |
Example:
POST /v1/knowledge/consolidate¶
Trigger a knowledge consolidation job that marks stale entries, deduplicates by content hash, clusters similar entries, and cleans up unused stale entries.
Authentication: Required (Admin)
Request Body: None
Response Body:
| Field | Type | Description |
|---|---|---|
stale_marked |
integer | Entries marked as stale |
duplicates_merged |
integer | Duplicate entries merged |
clusters_summarized |
integer | Similar entry clusters summarized |
entries_cleaned |
integer | Stale entries removed |
duration_seconds |
float | Job execution time |
timestamp |
string | ISO 8601 timestamp of the job run |
Example:
curl -X POST http://localhost:8000/v1/knowledge/consolidate \
-H "X-Membrain-API-Key: ck_live_a1b2c3d4..."
{
"stale_marked": 5,
"duplicates_merged": 3,
"clusters_summarized": 1,
"entries_cleaned": 2,
"duration_seconds": 4.32,
"timestamp": "2026-03-08T12:00:00+00:00"
}
Error Responses:
| Code | Condition |
|---|---|
401 |
API key required |
403 |
API key inactive |
503 |
Knowledge system not available |
5. Audit¶
Audit log data is exposed through the dashboard API. See Dashboard - Audit below.
6. Admin¶
All admin endpoints are prefixed with /api/admin and require a valid API key.
POST /api/admin/projects¶
Create a new project.
Authentication: Required (Admin)
Request Body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | Yes | Unique project slug (1-255 chars) | |
display_name |
string | Yes | Human-readable project name (1-255 chars) | |
default_rate_limit_rpm |
integer | No | 60 |
Default requests per minute for keys in this project |
default_budget_daily_usd |
float | No | 0.0 |
Default daily budget in USD (0 = unlimited) |
default_budget_monthly_usd |
float | No | 0.0 |
Default monthly budget in USD (0 = unlimited) |
allowed_models |
array of strings | No | null |
Restrict keys to these models (null = all allowed) |
Response Body (201 Created):
| Field | Type | Description |
|---|---|---|
id |
string | UUID of the created project |
name |
string | Project slug |
display_name |
string | Human-readable name |
is_active |
boolean | Whether the project is active |
default_rate_limit_rpm |
integer | Default RPM limit |
default_budget_daily_usd |
float | Default daily budget |
default_budget_monthly_usd |
float | Default monthly budget |
allowed_models |
array or null | Allowed models list |
created_at |
string | ISO 8601 timestamp |
Example:
curl -X POST http://localhost:8000/api/admin/projects \
-H "Content-Type: application/json" \
-H "X-Membrain-API-Key: ck_live_a1b2c3d4..." \
-d '{
"name": "engineering",
"display_name": "Engineering Team",
"default_rate_limit_rpm": 120,
"default_budget_daily_usd": 50.0,
"allowed_models": ["gpt-4", "claude-sonnet-4-20250514"]
}'
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"name": "engineering",
"display_name": "Engineering Team",
"is_active": true,
"default_rate_limit_rpm": 120,
"default_budget_daily_usd": 50.0,
"default_budget_monthly_usd": 0.0,
"allowed_models": ["gpt-4", "claude-sonnet-4-20250514"],
"created_at": "2026-03-08T12:00:00+00:00"
}
Error Responses:
| Code | Condition |
|---|---|
401 |
API key required or invalid |
409 |
Project with that name already exists |
503 |
Database not configured |
GET /api/admin/projects¶
List all projects.
Authentication: Required (Admin)
Response Body: Array of project objects (same schema as POST response).
Example:
[
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"name": "engineering",
"display_name": "Engineering Team",
"is_active": true,
"default_rate_limit_rpm": 120,
"default_budget_daily_usd": 50.0,
"default_budget_monthly_usd": 0.0,
"allowed_models": ["gpt-4", "claude-sonnet-4-20250514"],
"created_at": "2026-03-08T12:00:00+00:00"
}
]
POST /api/admin/keys¶
Create a new API key. The raw key is returned once and never stored or shown again.
Authentication: Required (Admin)
Request Body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | Yes | Human-readable key name (1-255 chars) | |
project |
string | Yes | Project slug the key belongs to (1-255 chars) | |
user_id |
string | No | null |
User this key is associated with |
rate_limit_rpm |
integer | No | null |
Per-key rate limit override (null = use project default) |
budget_daily_usd |
float | No | null |
Per-key daily budget override |
budget_monthly_usd |
float | No | null |
Per-key monthly budget override |
allowed_models |
array of strings | No | null |
Per-key model restrictions |
expires_at |
string (datetime) | No | null |
ISO 8601 expiration timestamp |
Response Body (201 Created):
| Field | Type | Description |
|---|---|---|
id |
string | UUID of the created API key |
raw_key |
string | The raw API key (shown only once) |
name |
string | Human-readable key name |
project |
string | Project slug |
created_at |
string | ISO 8601 timestamp |
Example:
curl -X POST http://localhost:8000/api/admin/keys \
-H "Content-Type: application/json" \
-H "X-Membrain-API-Key: ck_live_a1b2c3d4..." \
-d '{
"name": "alice-dev-key",
"project": "engineering",
"user_id": "alice",
"rate_limit_rpm": 30,
"budget_daily_usd": 10.0
}'
{
"id": "e7c8d9a0-b1c2-4d3e-8f5a-6b7c8d9e0f1a",
"raw_key": "ck_live_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"name": "alice-dev-key",
"project": "engineering",
"created_at": "2026-03-08T12:00:00+00:00"
}
Error Responses:
| Code | Condition |
|---|---|
401 |
API key required or invalid |
404 |
Project not found |
503 |
Database not configured |
GET /api/admin/keys¶
List all API keys (raw key values are never included).
Authentication: Required (Admin)
Response Body: Array of key summary objects:
| Field | Type | Description |
|---|---|---|
id |
string | UUID of the API key |
name |
string | Human-readable key name |
project |
string | Project slug |
user_id |
string or null | Associated user |
is_active |
boolean | Whether the key is active |
rate_limit_rpm |
integer or null | Per-key RPM limit |
budget_daily_usd |
float or null | Per-key daily budget |
budget_monthly_usd |
float or null | Per-key monthly budget |
allowed_models |
array or null | Per-key model restrictions |
created_at |
string | ISO 8601 creation timestamp |
expires_at |
string or null | ISO 8601 expiration timestamp |
Example:
[
{
"id": "e7c8d9a0-b1c2-4d3e-8f5a-6b7c8d9e0f1a",
"name": "alice-dev-key",
"project": "engineering",
"user_id": "alice",
"is_active": true,
"rate_limit_rpm": 30,
"budget_daily_usd": 10.0,
"budget_monthly_usd": null,
"allowed_models": null,
"created_at": "2026-03-08T12:00:00+00:00",
"expires_at": null
}
]
DELETE /api/admin/keys/{key_id}¶
Deactivate an API key (soft delete). The key is marked inactive but not removed from the database.
Authentication: Required (Admin)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
key_id |
string (UUID) | The UUID of the API key to deactivate |
Response Body (200 OK):
| Field | Type | Description |
|---|---|---|
detail |
string | Confirmation message |
Example:
curl -X DELETE http://localhost:8000/api/admin/keys/e7c8d9a0-b1c2-4d3e-8f5a-6b7c8d9e0f1a \
-H "X-Membrain-API-Key: ck_live_a1b2c3d4..."
Error Responses:
| Code | Condition |
|---|---|
400 |
Invalid key ID format |
401 |
API key required or invalid |
404 |
API key not found |
503 |
Database not configured |
7. Reports¶
All report endpoints are prefixed with /api/dashboard/reports.
GET /api/dashboard/reports/pii-summary¶
PII detection summary with category breakdown, daily trend, and top models.
Authentication: Optional
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
days |
integer | No | 30 |
Look-back window in days (1-365) |
project |
string | No | null |
Filter by project |
Response Body:
| Field | Type | Description |
|---|---|---|
total_detections |
integer | Total PII detections in the window |
categories |
array | PII category breakdown |
categories[].category |
string | PII category name (e.g., "EMAIL", "SSN") |
categories[].count |
integer | Number of detections |
categories[].percentage |
float | Percentage of total detections |
daily_trend |
array | Daily detection counts |
daily_trend[].date |
string | Date in YYYY-MM-DD format |
daily_trend[].count |
integer | Detections on that day |
top_models |
array | Models with most PII detections |
top_models[].model |
string | Model name |
top_models[].pii_count |
integer | Detection count |
Example:
{
"total_detections": 42,
"categories": [
{"category": "EMAIL", "count": 20, "percentage": 47.6},
{"category": "PHONE", "count": 12, "percentage": 28.6},
{"category": "SSN", "count": 10, "percentage": 23.8}
],
"daily_trend": [
{"date": "2026-03-02", "count": 5},
{"date": "2026-03-03", "count": 8},
{"date": "2026-03-04", "count": 6},
{"date": "2026-03-05", "count": 7},
{"date": "2026-03-06", "count": 9},
{"date": "2026-03-07", "count": 4},
{"date": "2026-03-08", "count": 3}
],
"top_models": [
{"model": "gpt-4", "pii_count": 25},
{"model": "claude-sonnet-4-20250514", "pii_count": 17}
]
}
GET /api/dashboard/reports/compliance¶
Full compliance report combining PII detections, request volume, cost, and model usage.
Authentication: Optional
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
days |
integer | No | 30 |
Look-back window in days (1-365) |
project |
string | No | null |
Filter by project |
Response Body:
| Field | Type | Description |
|---|---|---|
generated_at |
string | ISO 8601 generation timestamp |
date_range_start |
string | ISO 8601 start of the window |
date_range_end |
string | ISO 8601 end of the window |
total_requests |
integer | Total requests in the window |
unique_users |
integer | Count of distinct users |
pii_detection_rate |
float | Percentage of requests containing PII |
total_pii_detections |
integer | Total PII detections |
pii_categories |
array | PII category breakdown (same as PII summary) |
model_usage |
array | Per-model usage data |
model_usage[].model |
string | Model name |
model_usage[].requests |
integer | Request count |
model_usage[].cost_usd |
float | Total cost in USD |
total_cost_usd |
float | Total cost across all models |
daily_request_counts |
array | Daily request volume |
daily_request_counts[].date |
string | Date in YYYY-MM-DD format |
daily_request_counts[].count |
integer | Request count |
Example:
{
"generated_at": "2026-03-08T12:00:00+00:00",
"date_range_start": "2026-02-06T12:00:00+00:00",
"date_range_end": "2026-03-08T12:00:00+00:00",
"total_requests": 1250,
"unique_users": 15,
"pii_detection_rate": 12.5,
"total_pii_detections": 156,
"pii_categories": [
{"category": "EMAIL", "count": 80, "percentage": 51.3}
],
"model_usage": [
{"model": "gpt-4", "requests": 800, "cost_usd": 24.5},
{"model": "claude-sonnet-4-20250514", "requests": 450, "cost_usd": 13.2}
],
"total_cost_usd": 37.7,
"daily_request_counts": [
{"date": "2026-02-06", "count": 40},
{"date": "2026-02-07", "count": 45}
]
}
GET /api/dashboard/reports/export¶
Export a report as JSON or CSV.
Authentication: Optional
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
format |
string | No | "json" |
Export format: "json" or "csv" |
report |
string | No | "compliance" |
Report type: "compliance" or "pii-summary" |
days |
integer | No | 30 |
Look-back window in days (1-365) |
project |
string | No | null |
Filter by project |
Response:
- JSON format: Returns the report object directly (same schema as the corresponding report endpoint).
- CSV format: Returns a text/csv file download with Content-Disposition: attachment; filename="{report}-report.csv".
Example (JSON):
Example (CSV):
curl -o compliance-report.csv \
"http://localhost:8000/api/dashboard/reports/export?format=csv&report=compliance&days=30"
8. Shadow AI¶
Shadow AI detection endpoints are prefixed with /api/dashboard.
GET /api/dashboard/shadow¶
Full report of all detected AI tool usage across the organization. Tracks User-Agent strings, source IPs, and request patterns to identify both approved and unapproved AI tools.
Authentication: Optional
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
provider |
string | No | null |
Filter by provider name |
approved_only |
boolean | No | false |
Show only approved tools |
unapproved_only |
boolean | No | false |
Show only unapproved tools |
limit |
integer | No | 100 |
Max results to return (1-500) |
Response Body:
| Field | Type | Description |
|---|---|---|
tools |
array | List of detected tools (sorted by request count, descending) |
tools[].tool_name |
string | Classified tool name (e.g., "Cursor", "GitHub Copilot", "Claude CLI") |
tools[].user_agent |
string | Raw User-Agent string |
tools[].provider |
string | AI provider being called |
tools[].model |
string | Most recent model used |
tools[].source_ip |
string | Source IP address |
tools[].request_count |
integer | Total requests from this tool |
tools[].first_seen |
string | ISO 8601 timestamp of first detection |
tools[].last_seen |
string | ISO 8601 timestamp of most recent detection |
tools[].is_approved |
boolean | Whether the tool is in the approved list |
total_tools |
integer | Total number of unique tools detected |
total_requests |
integer | Total request count across all tools |
unapproved_count |
integer | Number of unapproved tools |
Recognized Tool Classifications:
| User-Agent pattern | Tool Name |
|---|---|
cursor |
Cursor |
copilot or github |
GitHub Copilot |
claude-cli or claude-code |
Claude CLI |
openai-python |
OpenAI Python SDK |
anthropic-python |
Anthropic Python SDK |
chatgpt |
ChatGPT |
mozilla, chrome, safari |
Browser |
python-httpx or python-requests |
Python HTTP Client |
node-fetch or axios |
Node.js HTTP Client |
| (other) | Unknown |
Example:
{
"tools": [
{
"tool_name": "Cursor",
"user_agent": "Cursor/0.45.1",
"provider": "openai",
"model": "gpt-4",
"source_ip": "10.0.1.42",
"request_count": 237,
"first_seen": "2026-03-01T08:15:00+00:00",
"last_seen": "2026-03-08T11:30:00+00:00",
"is_approved": false
}
],
"total_tools": 1,
"total_requests": 237,
"unapproved_count": 1
}
GET /api/dashboard/shadow/overview¶
High-level shadow AI overview for the dashboard, including per-provider breakdown.
Authentication: Optional
Response Body:
| Field | Type | Description |
|---|---|---|
total_unique_tools |
integer | Number of distinct tool types detected |
total_requests |
integer | Total requests across all shadow tools |
unapproved_tools |
integer | Number of unapproved tool types |
providers |
array | Per-provider statistics |
providers[].provider |
string | Provider name |
providers[].tool_count |
integer | Number of tool instances for this provider |
providers[].request_count |
integer | Total requests to this provider |
Example:
{
"total_unique_tools": 4,
"total_requests": 1520,
"unapproved_tools": 2,
"providers": [
{"provider": "anthropic", "tool_count": 3, "request_count": 890},
{"provider": "openai", "tool_count": 5, "request_count": 630}
]
}
9. Dashboard¶
All dashboard endpoints are prefixed with /api/dashboard.
GET /api/dashboard/overview¶
Aggregate overview statistics for the dashboard.
Authentication: Optional
Response Body:
| Field | Type | Description |
|---|---|---|
total_requests |
integer | Total requests processed |
total_cost_usd |
float | Estimated total cost in USD |
cache_hit_rate |
float | Cache hit rate (0.0 to 1.0) |
active_users |
integer | Number of active users (currently 0; reserved) |
pii_detections |
integer | Total PII detections across all requests |
Example:
{
"total_requests": 1250,
"total_cost_usd": 37.65,
"cache_hit_rate": 0.0,
"active_users": 0,
"pii_detections": 156
}
GET /api/dashboard/costs¶
Cost breakdown by provider and model.
Authentication: Optional
Response Body:
| Field | Type | Description |
|---|---|---|
breakdown |
array | Per-provider/model cost data |
breakdown[].provider |
string | Provider name |
breakdown[].model |
string | Model name |
breakdown[].total_cost_usd |
float | Total cost for this model |
breakdown[].total_tokens |
integer | Total tokens consumed |
breakdown[].request_count |
integer | Number of requests |
Example:
{
"breakdown": [
{
"provider": "openai",
"model": "gpt-4",
"total_cost_usd": 24.50,
"total_tokens": 125000,
"request_count": 800
},
{
"provider": "anthropic",
"model": "claude-sonnet-4-20250514",
"total_cost_usd": 13.15,
"total_tokens": 98000,
"request_count": 450
}
]
}
GET /api/dashboard/audit¶
Paginated audit log entries with optional filters.
Authentication: Optional
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page |
integer | No | 1 |
Page number (1-based) |
page_size |
integer | No | 50 |
Entries per page (1-100) |
user_id |
string | No | null |
Filter by user ID (reserved) |
project |
string | No | null |
Filter by project (reserved) |
provider |
string | No | null |
Filter by provider name |
Response Body:
| Field | Type | Description |
|---|---|---|
entries |
array | Audit log entries for the current page |
entries[].id |
string | Unique entry ID |
entries[].created_at |
string | ISO 8601 timestamp |
entries[].user_id |
string or null | User identifier |
entries[].project |
string or null | Project identifier |
entries[].provider |
string | Provider name |
entries[].model |
string | Model name |
entries[].prompt_tokens |
integer | Input tokens |
entries[].completion_tokens |
integer | Output tokens |
entries[].cost_usd |
float | Estimated cost in USD |
entries[].duration_ms |
integer | Request duration in milliseconds |
entries[].streaming |
boolean | Whether the request was streaming |
entries[].pii_findings_count |
integer | Number of PII items detected |
entries[].pii_labels |
array of strings | PII category labels (e.g., ["EMAIL_1", "SSN_1"]) |
entries[].pii_mapping |
object | Map of PII placeholders to original values |
total |
integer | Total number of entries (across all pages) |
page |
integer | Current page number |
page_size |
integer | Page size |
Example:
{
"entries": [
{
"id": "a1b2c3d4e5f6",
"created_at": "2026-03-08T12:00:00+00:00",
"user_id": "alice",
"project": "engineering",
"provider": "openai",
"model": "gpt-4",
"prompt_tokens": 150,
"completion_tokens": 80,
"cost_usd": 0.0069,
"duration_ms": 1230,
"streaming": false,
"pii_findings_count": 2,
"pii_labels": ["EMAIL_1", "PHONE_1"],
"pii_mapping": {
"[EMAIL_1]": "alice@example.com",
"[PHONE_1]": "555-0123"
}
}
],
"total": 1,
"page": 1,
"page_size": 10
}
GET /api/dashboard/pii¶
All PII values detected across requests, with category filtering.
Authentication: Optional
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
category |
string | No | null |
Filter by PII category (e.g., "EMAIL", "SSN") |
limit |
integer | No | 100 |
Max findings to return (1-500) |
Response Body:
| Field | Type | Description |
|---|---|---|
findings |
array | PII detection records |
findings[].placeholder |
string | PII placeholder used (e.g., "[EMAIL_1]") |
findings[].value |
string | Original PII value |
findings[].category |
string | PII category (e.g., "EMAIL") |
findings[].request_id |
string | Audit entry ID where PII was found |
findings[].model |
string | Model used in the request |
findings[].detected_at |
string | ISO 8601 detection timestamp |
total |
integer | Total findings returned |
Example:
{
"findings": [
{
"placeholder": "[EMAIL_1]",
"value": "alice@example.com",
"category": "EMAIL",
"request_id": "a1b2c3d4e5f6",
"model": "gpt-4",
"detected_at": "2026-03-08T12:00:00+00:00"
}
],
"total": 1
}
POST /api/dashboard/knowledge/search¶
Search knowledge entries from the dashboard. Uses semantic search via pgvector when available, falls back to in-memory text search.
Authentication: Optional
Request Body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
query |
string | Yes | Search query text | |
project |
string | No | null |
Filter by project |
limit |
integer | No | 10 |
Maximum results |
Response Body:
| Field | Type | Description |
|---|---|---|
results |
array | Matching knowledge entries |
results[].id |
string | Entry ID |
results[].content |
string | Entry content |
results[].user_id |
string or null | User identifier |
results[].project |
string or null | Project identifier |
results[].created_at |
string | ISO 8601 timestamp |
Example:
curl -X POST http://localhost:8000/api/dashboard/knowledge/search \
-H "Content-Type: application/json" \
-d '{"query": "PII detection", "limit": 5}'
{
"results": [
{
"id": "a1b2c3d4e5f6",
"content": "Membrain supports PII detection for 25+ patterns...",
"user_id": null,
"project": "docs",
"created_at": "2026-03-08T12:00:00+00:00"
}
]
}
GET /api/dashboard/knowledge¶
Browse knowledge entries with pagination. Uses pgvector store when available, falls back to in-memory.
Authentication: Optional
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page |
integer | No | 1 |
Page number (1-based) |
page_size |
integer | No | 20 |
Entries per page (1-100) |
project |
string | No | null |
Filter by project |
Response Body:
| Field | Type | Description |
|---|---|---|
entries |
array | Knowledge entries for the current page |
entries[].id |
string | Entry ID |
entries[].content |
string | Entry content |
entries[].user_id |
string or null | User identifier |
entries[].project |
string or null | Project identifier |
entries[].created_at |
string | ISO 8601 timestamp |
total |
integer | Total number of entries |
page |
integer | Current page number |
page_size |
integer | Page size |
Example:
{
"entries": [
{
"id": "a1b2c3d4e5f6",
"content": "Example knowledge entry...",
"user_id": null,
"project": "docs",
"created_at": "2026-03-08T12:00:00+00:00"
}
],
"total": 1,
"page": 1,
"page_size": 10
}
POST /api/dashboard/knowledge¶
Add a knowledge entry from the dashboard UI.
Authentication: Optional
Request Body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
content |
string | Yes | Knowledge content text | |
user_id |
string | No | null |
User identifier |
project |
string | No | null |
Project identifier |
Response Body (201 Created):
| Field | Type | Description |
|---|---|---|
id |
string | Entry ID |
content |
string | Stored content |
user_id |
string or null | User identifier |
project |
string or null | Project identifier |
created_at |
string | ISO 8601 timestamp |
Example:
curl -X POST http://localhost:8000/api/dashboard/knowledge \
-H "Content-Type: application/json" \
-d '{"content": "New knowledge entry from dashboard", "project": "docs"}'
{
"id": "b2c3d4e5f6a1",
"content": "New knowledge entry from dashboard",
"user_id": null,
"project": "docs",
"created_at": "2026-03-08T12:00:00+00:00"
}
DELETE /api/dashboard/knowledge/{entry_id}¶
Delete a knowledge entry from the dashboard.
Authentication: Optional
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
entry_id |
string | Entry ID (UUID for DB entries, short hex for in-memory) |
Response: 204 No Content on success.
Example:
Error Responses:
| Code | Condition |
|---|---|
404 |
Knowledge entry not found |
GET /api/dashboard/cache¶
Cache performance statistics derived from audit data.
Authentication: Optional
Response Body:
| Field | Type | Description |
|---|---|---|
exact_hit_rate |
float | Exact-match cache hit rate (0.0 to 1.0) |
semantic_hit_rate |
float | Semantic cache hit rate (0.0 to 1.0) |
total_cached |
integer | Total number of cached responses |
estimated_savings_usd |
float | Estimated cost savings from cache hits |
Example:
{
"exact_hit_rate": 0.15,
"semantic_hit_rate": 0.0,
"total_cached": 45,
"estimated_savings_usd": 1.23
}
10. Metrics¶
GET /metrics¶
Expose collected metrics in Prometheus exposition format. Includes counters, histograms, and gauges tracked by the ASGI telemetry middleware.
Authentication: None
Response: Plain text in Prometheus exposition format with content type text/plain; version=0.0.4.
Example:
# HELP http_requests_total Total HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="POST",path="/v1/chat/completions",status="200"} 1250
http_requests_total{method="POST",path="/v1/messages",status="200"} 430
# HELP http_request_duration_seconds HTTP request duration
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.1"} 200
http_request_duration_seconds_bucket{le="0.5"} 800
http_request_duration_seconds_bucket{le="1.0"} 1100
http_request_duration_seconds_bucket{le="+Inf"} 1250
http_request_duration_seconds_sum 625.4
http_request_duration_seconds_count 1250
Endpoint Summary¶
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/health |
None | Health check |
GET |
/metrics |
None | Prometheus metrics |
POST |
/v1/chat/completions |
None | OpenAI-compatible chat completions |
POST |
/v1/messages |
Anthropic key | Anthropic Messages API proxy |
POST |
/v1/messages/count_tokens |
Anthropic key | Anthropic token counting proxy |
POST |
/v1/knowledge |
None | Add knowledge entry |
GET |
/v1/knowledge/search |
None | Semantic knowledge search |
DELETE |
/v1/knowledge/{entry_id} |
None | Delete knowledge entry |
POST |
/v1/knowledge/ingest |
None | Bulk ingest knowledge |
GET |
/v1/knowledge/topics |
None | List knowledge topics |
POST |
/v1/knowledge/consolidate |
Admin | Trigger knowledge consolidation |
POST |
/api/admin/projects |
Admin | Create project |
GET |
/api/admin/projects |
Admin | List projects |
POST |
/api/admin/keys |
Admin | Create API key |
GET |
/api/admin/keys |
Admin | List API keys |
DELETE |
/api/admin/keys/{key_id} |
Admin | Deactivate API key |
GET |
/api/dashboard/overview |
Optional | Dashboard overview stats |
GET |
/api/dashboard/costs |
Optional | Cost breakdown |
GET |
/api/dashboard/audit |
Optional | Paginated audit log |
GET |
/api/dashboard/pii |
Optional | PII findings |
POST |
/api/dashboard/knowledge/search |
Optional | Dashboard knowledge search |
GET |
/api/dashboard/knowledge |
Optional | Browse knowledge entries |
POST |
/api/dashboard/knowledge |
Optional | Add knowledge (dashboard) |
DELETE |
/api/dashboard/knowledge/{entry_id} |
Optional | Delete knowledge (dashboard) |
GET |
/api/dashboard/cache |
Optional | Cache statistics |
GET |
/api/dashboard/shadow |
Optional | Shadow AI detection report |
GET |
/api/dashboard/shadow/overview |
Optional | Shadow AI overview |
GET |
/api/dashboard/reports/pii-summary |
Optional | PII summary report |
GET |
/api/dashboard/reports/compliance |
Optional | Full compliance report |
GET |
/api/dashboard/reports/export |
Optional | Export report (JSON/CSV) |