Skip to content

MemBrain API Reference

Complete reference for every HTTP endpoint exposed by the MemBrain gateway.

Base URL: http://localhost:8000 (default)


Table of Contents

  1. Authentication
  2. Rate Limit Headers
  3. Error Response Format
  4. Health
  5. Chat Completions
  6. Anthropic Proxy
  7. Knowledge (governance)
  8. Audit
  9. Admin
  10. Reports
  11. Shadow AI
  12. Dashboard
  13. Metrics
  14. Learning Brain
  15. Team-brain MCP server

Authentication

MemBrain uses API keys for authentication, validated against the api_keys table. The installer's secure-by-default setup sets REQUIRE_AUTH=true on both gateways and bootstraps your first key via membrain key create; keyless requests to any auth-gated endpoint get a 401. If a database isn't configured at all, key-gated endpoints fail closed with a 503 rather than silently allowing the request through.

API Key Format

Keys follow the format ck_live_<32 hex characters>, for example:

ck_live_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4

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 (e.g. GET /health).
Required A valid membrain key is mandatory (require_api_key). Returns 401 if missing/invalid, 403 if inactive/expired, 503 if no database is configured. Used by all /v1/knowledge/* endpoints.
Required (sensitive) A valid membrain key is mandatory whenever REQUIRE_AUTH=true (require_sensitive_auth) — the default for installer deploys. Only when an operator explicitly sets REQUIRE_AUTH=false does the endpoint fall back to an unauthenticated viewer-role sentinel. Used by /v1/chat/completions, /v1/messages, /v1/messages/count_tokens, /metrics, and all /api/dashboard/* endpoints.

Endpoint-level auth requirements are unrelated to the provider credentials some endpoints also require (e.g. the Anthropic proxy's X-API-Key header, forwarded upstream) — see each endpoint below for both.


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)

{
  "detail": "Human-readable error message"
}

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)

{
  "type": "error",
  "error": {
    "type": "authentication_error",
    "message": "Missing API key"
  }
}

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:

curl http://localhost:8000/health
{
  "status": "ok"
}

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: Required (sensitive) — a valid membrain key via X-Membrain-API-Key or Authorization: Bearer is mandatory whenever REQUIRE_AUTH=true (the installer default). Only when an operator explicitly sets REQUIRE_AUTH=false does the endpoint fall back to unauthenticated access.

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: Two independent gates. (1) A membrain key is Required (sensitive) — same REQUIRE_AUTH behavior as /v1/chat/completions above. (2) An Anthropic API key via X-API-Key header, Authorization header, or server-configured ANTHROPIC_API_KEY is still needed for the upstream provider call.

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 two gates as /v1/messages — a membrain key (Required (sensitive)) plus the 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!"}
    ]
  }'
{
  "input_tokens": 12
}

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: Required — a valid membrain key (require_api_key)

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: Required — a valid membrain key (require_api_key)

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:

curl "http://localhost:8000/v1/knowledge/search?q=PII+detection&limit=3"
{
  "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: Required — a valid membrain key (require_api_key)

Path Parameters:

Parameter Type Description
entry_id string (UUID) The UUID of the knowledge entry to delete

Response: 204 No Content on success.

Example:

curl -X DELETE http://localhost:8000/v1/knowledge/a1b2c3d4-e5f6-7890-abcd-ef1234567890

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: Required — a valid membrain key (require_api_key)

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: Required — a valid membrain key (require_api_key)

Query Parameters:

Parameter Type Required Default Description
project string No null Filter topics to a single project

Response Body:

Field Type Description
topics array of strings Sorted list of all unique topics

Example:

curl "http://localhost:8000/v1/knowledge/topics?project=docs" \
  -H "X-Membrain-API-Key: ck_live_a1b2c3d4..."
{
  "topics": ["architecture", "deployment", "pii-detection", "security"]
}

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 (any key — admin gate is a known follow-up)

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

Knowledge governance

Every knowledge entry carries a status (pendingapproved or rejected) and, once approved, a visibility (private or team). Entries reach the pending queue two ways — a deliberate save (via POST /v1/knowledge, /v1/knowledge/ingest, or the team-brain MCP save_to_team_brain tool) or a daily reflected lesson proposed by the reflector, each citing the episodes it derives from. Under the default strict-approval mode, new entries land pending / private: they are never returned by /v1/knowledge/search or injected into a request until a human approves them. Approved entries stay private to their owner until the owner explicitly shares them; shared (team) entries carry owner attribution in the injection manifest and chat display. Rejected entries are soft-quarantined (tombstoned) rather than hard-deleted, so they can be restored within a recovery window.

The dashboard endpoints below require sensitive-auth (an operator/admin session); the /v1/knowledge/* endpoints below require an API key, with flagged / quarantine / quarantined restricted to admin or operator roles.

GET /api/dashboard/knowledge/pending

List pending entries awaiting human review, scoped to the caller's project.

Query Parameters: project, source, limit (default 50), offset (default 0)

Response Body: entries[] with id, content, project, source, extraction_score, created_at, pending_expires_at

POST /api/dashboard/knowledge/{entry_id}/approve

Promote a pending entry to approved, making it searchable and eligible for injection. Returns {"status": "approved", "id": ...}. 404 if no matching pending entry.

POST /api/dashboard/knowledge/{entry_id}/reject

Reject an entry — pending or approved — deleting it and leaving a tombstone. Optional body: {"reason": "..."}. Returns {"status": "rejected", "id": ...}.

POST /v1/knowledge/{entry_id}/share and POST /api/dashboard/knowledge/{entry_id}/share

Owner-only. Flips visibility to team, making the entry visible to the rest of the tenant. Returns {"status": "shared", "id": ...}.

Error Responses:

Code Condition
403 Caller is not the entry's owner
404 Entry not found
409 Entry is not yet approved — share is only allowed on approved entries

POST /v1/knowledge/{entry_id}/unshare and POST /api/dashboard/knowledge/{entry_id}/unshare

Owner-only. Reverts visibility to private. Returns {"status": "private", "id": ...}. Same error responses as share, minus the 409 (an approved entry can always be made private again).

GET /v1/knowledge/flagged

List entries the AI consolidation judge flagged for human review (would-quarantine / would-consolidate), each with the model's reason.

Query Parameters: project, limit (default 100), include_approved (default false — set true to also surface flags raised against already-approved entries)

Response Body: entries[] with id, content, project, flagged_at, flagged_reason, source, status

POST /v1/knowledge/{entry_id}/unflag

Clear an AI flag without quarantining ("Keep" action) — the entry stays in its current status. Returns {"status": "unflagged", "id": ...}. 404 if not flagged or cross-tenant.

POST /v1/knowledge/{entry_id}/quarantine

Soft-quarantine a flagged entry after human review (always human-initiated, never called by the judge). Optional body: {"expected_status": "pending" | "approved"} (default "pending") — a TOCTOU guard that 404s if the entry's actual status no longer matches, so a concurrent human action wins. Returns {"status": "quarantined", "id": ...}.

GET /v1/knowledge/quarantined

List recently quarantined entries for the dashboard's Quarantined tab.

Query Parameters: project, limit (default 100)

Response Body: entries[] with id, content, project, rejected_at, rejected_reason, source

POST /v1/knowledge/quarantined/{entry_id}/restore

Restore a quarantined entry back to pending within its recovery window. Returns {"status": "restored", "id": ...}. 404 if the entry isn't currently quarantined.


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:

curl http://localhost:8000/api/admin/projects \
  -H "X-Membrain-API-Key: ck_live_a1b2c3d4..."
[
  {
    "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:

curl http://localhost:8000/api/admin/keys \
  -H "X-Membrain-API-Key: ck_live_a1b2c3d4..."
[
  {
    "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..."
{
  "detail": "API key 'alice-dev-key' deactivated."
}

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:

curl "http://localhost:8000/api/dashboard/reports/pii-summary?days=7"
{
  "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:

curl "http://localhost:8000/api/dashboard/reports/compliance?days=30&project=engineering"
{
  "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):

curl "http://localhost:8000/api/dashboard/reports/export?format=json&report=pii-summary&days=7"

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:

curl "http://localhost:8000/api/dashboard/shadow?unapproved_only=true"
{
  "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:

curl http://localhost:8000/api/dashboard/shadow/overview
{
  "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:

curl http://localhost:8000/api/dashboard/overview
{
  "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:

curl http://localhost:8000/api/dashboard/costs
{
  "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:

curl "http://localhost:8000/api/dashboard/audit?page=1&page_size=10&provider=openai"
{
  "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:

curl "http://localhost:8000/api/dashboard/pii?category=EMAIL&limit=5"
{
  "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:

curl "http://localhost:8000/api/dashboard/knowledge?page=1&page_size=10"
{
  "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:

curl -X DELETE http://localhost:8000/api/dashboard/knowledge/a1b2c3d4e5f6

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:

curl http://localhost:8000/api/dashboard/cache
{
  "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: Required (sensitive) — a valid membrain key is mandatory whenever REQUIRE_AUTH=true (the installer default)

Response: Plain text in Prometheus exposition format with content type text/plain; version=0.0.4.

Example:

curl http://localhost:8000/metrics
# 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

11. Learning Brain

Read surfaces over the episodic memory layer (see Configuration → Learning loop). All endpoints below are prefixed /api/dashboard and require sensitive auth (a membrain key, mandatory whenever REQUIRE_AUTH=true).

Owner-clamping: episodes and executions are strictly owner-private — there is no admin-sees-all mode, in v1 or ever intended. The clamp is always the authenticated caller's own identity, enforced in the store's SQL rather than as a post-filter. A caller with no server-derived identity (e.g. a community/no-auth key with no user_id) gets a 400, not an empty result and not everyone's data. The one deliberate exception is the review agenda below, which intentionally shows every owner's approved-but-private backlog to every reviewer — that's the review ceremony's whole point, and it grants no new write power.

GET /api/dashboard/episodes/search

Semantic search over the caller's own episodic history (proxied exchanges and tool calls).

Authentication: Required (sensitive), owner-clamped

Query Parameters:

Parameter Type Required Default Description
query string Yes Search query (min 1 character)
limit integer No 10 Max results (1-50)
days integer No 90 Lookback window in days (1-3650)
kind string No null Filter by episode kind (e.g. "exchange", "execution")

Response Body: results[] with id, project, kind, user_digest, response_digest, created_at, audit_log_id, similarity.

Error Responses:

Code Condition
400 Caller has no server-derived owner identity (user_id not set on the key)
502 Episode search failed

GET /api/dashboard/executions

Timeline of the caller's own captured tool-call events, grouped by session.

Authentication: Required (sensitive), owner-clamped

Query Parameters:

Parameter Type Required Default Description
date string (YYYY-MM-DD) No today (UTC) Day to list events for
project string No null Filter by project
session_key string No null Filter to a single session
errors_only boolean No false Only include error events
limit integer No 200 Max events (1-1000)
offset integer No 0 Pagination offset

Response Body: date, groups[] — each group is {session_key, events[]} with events carrying id, seq, tool_name, tool_input, result_digest, is_error, created_at, audit_log_id.

Error Responses:

Code Condition
400 No owner identity, or date isn't a valid YYYY-MM-DD string
502 Query failed

GET /api/dashboard/executions/dates

Distinct-day index feeding the executions timeline's date picker.

Query Parameters: limit (default 30, 1-365)

Response Body: {"dates": [...]} — sorted list of ISO date strings with at least one captured event, owner-clamped.

GET /api/dashboard/executions/export

Download a single session's captured tool-call events as an annotated Markdown script — reconstruction only, never execution.

Query Parameters: date (required, YYYY-MM-DD), session_key (required), project (optional)

Response: text/markdown file download, Content-Disposition: attachment; filename="session-{date}-{hash8}.md" (hash8 is the first 8 hex chars of sha256(session_key) — the session key itself is never put in a filename). Capped at 5000 events per export.

Error Responses:

Code Condition
400 No owner identity, or date isn't a valid YYYY-MM-DD string
404 No matching events for that date + session_key (covers both "never existed" and "belongs to someone else" identically, so the 404 leaks nothing)

GET /api/dashboard/review/agenda

The team review ceremony's agenda — approved, private, not-yet-reviewed entries across the whole tenant, grouped by owner.

Authentication: Required (sensitive)

Deliberate exception to owner-clamping: every sensitive-auth viewer sees every owner's approved-but-private entries here, content included — the review ceremony is the consent moment the private default defers to. This grants no new write power: share / keep / archive below still enforce owner-only checks, so being visible on the agenda never lets anyone but the entry's owner act on it. An entry carries over to every future agenda until its own state changes (shared, kept, or archived) — it never silently ages out.

Response Body: since (ISO timestamp of the last completed review session, or epoch if none has been held), groups[] — each group is {owner, count, entries[]}, entries carrying the usual knowledge-entry fields plus is_new (created_at > since).

POST /api/dashboard/review/complete

Batch-decide the review agenda and log the ceremony.

Authentication: Required (sensitive)

Request Body:

Field Type Required Description
decisions array Yes Up to 500 {id, action} pairs, action"share", "keep", "archive"
notes string No Optional free-text note logged with the review session

share routes through the same owner-only, approved-only set_visibility call the dashboard's Share button uses. archive is owner-clamped at this route (the underlying flag-for-review call has no owner concept of its own) — a viewer can only archive their own entries, even though the agenda shows everyone's. A single decision failing (wrong owner, status changed since the agenda was rendered, bad id) doesn't abort the batch — it's collected into errors and the rest still runs.

Response Body: items_shared, items_kept, items_archived, errors[] (each {id, action, detail}).


GET /api/dashboard/brain/growth

One-glance "is the brain learning?" card — aggregate counts only, no content exposed, no owner clamp needed.

Authentication: Required (sensitive)

Response Body:

Field Type Description
episodes_7d integer Episodes captured in the last 7 days (tenant-wide)
reflections_pending integer Reflector-proposed entries still pending review
approved_total integer Total approved knowledge entries
approved_delta_7d integer Approved entries added in the last 7 days
shared_total integer Total entries shared with the team
shared_delta_7d integer Entries shared in the last 7 days
decay_flags_open integer Open decay-review flags
injection_rate_7d float Approximate injections/day over the last 7 days
last_review object or null {held_at, items_shared} for the most recent review ceremony

12. Team-brain MCP server

MemBrain registers a built-in "team-brain" MCP server, answered in-process (no upstream connection or HTTP hop) against the local knowledge and episode stores. It's reached through the existing MCP proxy at POST /mcp/v1/team-brain, gated by the same Required (sensitive) auth as the rest of the MCP proxy, and enabled by default (MCP_TEAM_BRAIN_ENABLED=true, requires MCP_ENABLED=true).

Access: self-hosted network only — there is no public endpoint for this server. It's reachable only by clients (Claude Code, Claude Desktop, etc.) configured to talk to your own gateway's MCP proxy.

Tool Purpose
search_team_knowledge Search the team's shared (approved, team-visibility) knowledge base. Params: query (required), limit (default 5).
save_to_team_brain Save a fact to the knowledge store. Lands pending / private — queued for the caller's own review before anyone else can see it. Params: content (required), summary, topics.
list_my_pending List the caller's own entries still awaiting review. Params: limit (default 10).
recall_episodes Recall the caller's own past sessions (never another user's) — dated digests of past exchanges and tool executions. Params: query (required), limit (default 5), days (default 90), kind (optional: "exchange" or "execution").

save_to_team_brain writes through the same pending/private → approved → shared two-gate model as the REST API — see Knowledge governance.


Endpoint Summary

Method Path Auth Description
GET /health None Health check
GET /metrics Sensitive Prometheus metrics
POST /v1/chat/completions Sensitive OpenAI-compatible chat completions
POST /v1/messages Sensitive + Anthropic key Anthropic Messages API proxy
POST /v1/messages/count_tokens Sensitive + Anthropic key Anthropic token counting proxy
POST /v1/knowledge API key Add knowledge entry
GET /v1/knowledge/search API key Semantic knowledge search
DELETE /v1/knowledge/{entry_id} API key Delete knowledge entry
POST /v1/knowledge/ingest API key Bulk ingest knowledge
GET /v1/knowledge/topics API key List knowledge topics
POST /v1/knowledge/consolidate API key (admin gate: follow-up) Trigger knowledge consolidation
GET /api/dashboard/knowledge/pending Sensitive List pending knowledge entries
POST /api/dashboard/knowledge/{entry_id}/approve Sensitive Approve pending entry
POST /api/dashboard/knowledge/{entry_id}/reject Sensitive Reject entry (with reason)
POST /v1/knowledge/{entry_id}/share API key (owner) Share entry with team
POST /v1/knowledge/{entry_id}/unshare API key (owner) Revert entry to private
POST /api/dashboard/knowledge/{entry_id}/share Sensitive (owner) Share entry with team (dashboard)
POST /api/dashboard/knowledge/{entry_id}/unshare Sensitive (owner) Revert entry to private (dashboard)
GET /v1/knowledge/flagged Admin/operator List AI-flagged entries
POST /v1/knowledge/{entry_id}/unflag Admin/operator Clear an AI flag
POST /v1/knowledge/{entry_id}/quarantine Admin/operator Quarantine a flagged entry
GET /v1/knowledge/quarantined Admin/operator List quarantined entries
POST /v1/knowledge/quarantined/{entry_id}/restore Admin/operator Restore quarantined entry to pending
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)
GET /api/dashboard/episodes/search Sensitive (owner-clamped) Semantic search over the caller's own episodes
GET /api/dashboard/executions Sensitive (owner-clamped) Session-grouped tool-call timeline
GET /api/dashboard/executions/dates Sensitive (owner-clamped) Distinct-day index for the timeline
GET /api/dashboard/executions/export Sensitive (owner-clamped) Markdown export of one session
GET /api/dashboard/review/agenda Sensitive Team review ceremony agenda
POST /api/dashboard/review/complete Sensitive Batch-decide the review agenda
GET /api/dashboard/brain/growth Sensitive Aggregate "is the brain learning?" counts
POST /mcp/v1/team-brain Sensitive Built-in team-brain MCP server (JSON-RPC)