Agent Keys
Agent Keys are user-owned, scope-restricted API keys designed for AI agents and automated workflows. Each key is scoped to specific services and namespaces, supports optional TTL expiry, and can carry a fine-grained RBAC permission manifest that controls per-tool, per-namespace, and per-route access.
Key properties:
- Format:
ak_followed by atoken_urlsafe(32)string (e.g.,ak_dGhpcyBpcyBhIHRlc3Qga2V5...) - Storage: Only the SHA-256 hash is persisted. The raw key is returned exactly once at creation and cannot be recovered.
- Expiry: Keys with an
expires_attimestamp past the current time are rejected with 401. - Limit: Maximum 100 active keys per user.
Authentication
All endpoints require a valid session (JWT bearer token or existing API key) via the unified auth middleware.
Base URL
/api/v1/auth
Scope Format
Scopes follow the pattern <service>:<permission>[:<namespace>]:
zerodb:read:project/my-project
zerodb:write:project/my-project
inference:read
memory:write:session:abc123
Valid services: zerodb, inference, memory, file, agent, mcp
Namespace is optional. When present, it restricts the key to a specific data partition.
Permission Manifest (RBAC)
A permission manifest provides fine-grained access control beyond scopes. All fields are optional; omitting a field means unrestricted access for that dimension.
{
"allowed_tools": ["zerodb_store_memory", "zerodb_recall"],
"allowed_namespaces": ["project/my-project"],
"denied_routes": ["/api/v1/billing/**", "/api/v1/admin/**"],
"max_memory_bytes": 1048576
}
| Field | Type | Description |
|---|---|---|
allowed_tools | string[] | Whitelist of MCP tool names this key may invoke. Omit to allow all. |
allowed_namespaces | string[] | Whitelist of namespaces. Valid formats: global, project:<name>, project/<name>, session:<name>. Omit to allow all. |
denied_routes | string[] | Glob patterns for routes this key is denied. Supports ** wildcard (e.g., /api/v1/billing/**). |
max_memory_bytes | integer | Maximum memory the key may store, in bytes. Maximum: 104857600 (100 MB). |
Permission evaluation order:
- Key must exist and be active (not revoked, not expired).
- If
allowed_toolsis set and the requested tool is not in the list, the request is denied. - If
allowed_namespacesis set and the requested namespace is not in the list, the request is denied. - If
denied_routescontains a pattern matching the request path, the request is denied. - Otherwise, the request is allowed.
An empty manifest {} grants unrestricted access.
Endpoints
POST /api/v1/auth/keys
Create a new agent-scoped API key.
Request body:
{
"name": "ci-agent-key",
"scopes": ["zerodb:read:project/my-project", "zerodb:write:project/my-project"],
"ttl_seconds": 86400,
"permissions": {
"allowed_tools": ["zerodb_store_memory", "zerodb_recall"],
"allowed_namespaces": ["project/my-project"],
"denied_routes": ["/api/v1/billing/**"],
"max_memory_bytes": 1048576
}
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Human-readable label (1-128 characters) |
scopes | string[] | Yes | At least one scope in service:permission[:namespace] format |
ttl_seconds | integer | No | Seconds until expiry. Omit for a non-expiring key. |
permissions | object | No | RBAC permission manifest. Omit for unrestricted access. |
Response (201 Created):
{
"id": "a1b2c3d4-...",
"user_id": "u-5678-...",
"name": "ci-agent-key",
"key": "ak_dGhpcyBpcyBhIHRlc3Qga2V5...",
"scopes": ["zerodb:read:project/my-project", "zerodb:write:project/my-project"],
"permissions": {
"allowed_tools": ["zerodb_store_memory", "zerodb_recall"],
"allowed_namespaces": ["project/my-project"],
"denied_routes": ["/api/v1/billing/**"],
"max_memory_bytes": 1048576
},
"expires_at": "2026-06-02T12:00:00+00:00",
"last_used_at": null,
"is_active": true,
"created_at": "2026-06-01T12:00:00+00:00"
}
The key field is only returned at creation time. Store it immediately.
GET /api/v1/auth/keys
List API keys for the authenticated user.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
include_inactive | boolean | false | Include revoked/expired keys |
limit | integer | 50 | Max results (1-200) |
offset | integer | 0 | Pagination offset |
Response (200 OK):
[
{
"id": "a1b2c3d4-...",
"user_id": "u-5678-...",
"name": "ci-agent-key",
"scopes": ["zerodb:read:project/my-project"],
"permissions": {},
"expires_at": "2026-06-02T12:00:00+00:00",
"last_used_at": "2026-06-01T14:30:00+00:00",
"is_active": true,
"created_at": "2026-06-01T12:00:00+00:00"
}
]
The raw key is never included in list responses.
DELETE /api/v1/auth/keys/{key_id}
Permanently revoke a key. This cannot be undone.
Response: 204 No Content
Errors:
| Status | Condition |
|---|---|
| 404 | Key not found or already revoked |
GET /api/v1/auth/keys/{key_id}/permissions
Return the RBAC permission manifest for a key that belongs to the authenticated user.
Response (200 OK):
{
"allowed_tools": ["zerodb_store_memory"],
"allowed_namespaces": ["project/my-project"],
"denied_routes": ["/api/v1/billing/**"],
"max_memory_bytes": 1048576
}
An empty object {} means the key has unrestricted access.
POST /api/v1/auth/keys/{key_id}/check-permission
Evaluate a key's RBAC manifest against a specific tool, namespace, or route.
Request body:
{
"tool": "zerodb_store_memory",
"namespace": "project/my-project",
"route": "/api/v1/memory/v2/remember"
}
All fields are optional. Only the supplied fields are checked.
Response (200 OK):
{
"allowed": true,
"reason": "all checks passed"
}
When denied:
{
"allowed": false,
"reason": "tool 'zerodb_delete' not in allowed_tools"
}
Key Validation at Request Time
When an agent presents a key in the Authorization header, the validation logic:
- Confirms the key starts with
ak_. - Computes
SHA-256(raw_key)and looks up the hash inagent_api_keys_v2. - Checks
is_active = true. - Checks
expires_atis null or in the future. - Updates
last_used_aton successful validation. - Returns the key's metadata (user_id, scopes, permissions) to the caller.
Error Responses
| Status | Meaning |
|---|---|
| 401 | Missing or invalid authentication |
| 404 | Key not found or does not belong to the user |
| 422 | Validation error (invalid scopes, permissions, or name) |