MemBrain API Reference¶
Complete reference for every HTTP endpoint exposed by the MemBrain gateway.
Base URL: http://localhost:8000 (default)
Table of Contents¶
- Authentication
- Rate Limit Headers
- Error Response Format
- Health
- Chat Completions
- Anthropic Proxy
- Knowledge (governance)
- Audit
- Admin
- Reports
- Shadow AI
- Dashboard
- Metrics
- Learning Brain
- 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:
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)¶
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: 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!"}
]
}'
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:
{
"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:
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..."
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 (pending → approved 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:
[
{
"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: 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:
# 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) |