Admin API Reference¶
The admin API runs on a separate port (default 3000) from the proxy. All endpoints require the x-api-key header with a valid admin API key. The key comparison uses constant-time equality to prevent timing side-channel attacks.
Authentication¶
Every request must include:
x-api-key: <your-admin-api-key>
Missing or invalid keys return 401.
Agents¶
Register Agent¶
POST /agents
Request Body:
{
"owner": "user:alice",
"model": "gpt-4",
"capabilities": ["read", "write"],
"trust_level": "basic",
"expires_at": "2026-04-15T00:00:00Z"
}
Field |
Type |
Required |
Description |
|---|---|---|---|
|
string |
yes |
Human principal (OAuth subject) |
|
string |
yes |
LLM model identifier |
|
string[] |
yes |
Agent capabilities |
|
string |
yes |
|
|
string |
no |
ISO 8601 expiration timestamp |
Response (201):
{
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}
The response includes a short-lived JWT for immediate use.
List Agents¶
GET /agents
Response (200):
[
{
"id": "550e8400-...",
"owner": "user:alice",
"model": "gpt-4",
"capabilities": ["read", "write"],
"trust_level": "basic",
"active": true,
"created_at": "2026-03-15T10:00:00Z"
}
]
Get Agent¶
GET /agents/{id}
Response (200): Same schema as list items. Returns 404 if not found.
Deactivate Agent¶
DELETE /agents/{id}
Deactivates the agent and cascade-deactivates all delegates in the delegation chain. This is not reversible.
Response (200):
{
"deactivated": ["550e8400-...", "661f9400-..."]
}
Returns the list of all deactivated agent IDs (the target plus its delegates).
Delegation¶
Create Delegation¶
POST /agents/{id}/delegate
Request Body:
{
"to": "661f9400-e29b-41d4-a716-446655440001",
"scopes": ["read"],
"expires_at": "2026-04-01T00:00:00Z"
}
Field |
Type |
Required |
Description |
|---|---|---|---|
|
UUID |
yes |
Target agent ID |
|
string[] |
yes |
Capabilities to delegate (must be subset of parent’s) |
|
string |
no |
Delegation expiration |
Response (201):
{
"delegation_id": "d1e2f3a4-..."
}
Scope narrowing is enforced: the delegated scopes must be a subset of the delegating agent’s capabilities. Attempting to widen scope returns 400 with ScopeNarrowingViolation.
List Delegations¶
GET /agents/{id}/delegations
Returns incoming and outgoing delegation links for the specified agent.
Tokens¶
Issue Token¶
POST /agents/{id}/token
Request Body:
{
"expiry_seconds": 300
}
Response (200):
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}
Issues a short-lived JWT signed with the configured signing secret. The token contains the agent ID, owner (sub), issuer, and expiration.
Sessions¶
Create Session¶
POST /sessions
Request Body:
{
"agent_id": "550e8400-...",
"declared_intent": "analyze transactions and generate reports",
"authorized_tools": ["query_transactions", "generate_risk_report"],
"time_limit_secs": 1800,
"call_budget": 100,
"rate_limit_per_minute": 30,
"data_sensitivity": "internal"
}
Field |
Type |
Required |
Description |
|---|---|---|---|
|
UUID |
yes |
Agent this session belongs to |
|
string |
yes |
Free-form intent description |
|
string[] |
yes |
Tool whitelist |
|
integer |
no |
Session duration (default from config) |
|
integer |
no |
Max tool calls (default from config) |
|
integer |
no |
Per-minute rate cap |
|
string |
no |
|
Response (201):
{
"session_id": "b7d3f1a2-..."
}
Returns 429 TooManySessions if the agent has reached max_concurrent_sessions_per_agent.
Get Session Status¶
GET /sessions/{id}
Response (200):
{
"session_id": "b7d3f1a2-...",
"agent_id": "550e8400-...",
"declared_intent": "analyze transactions",
"authorized_tools": ["query_transactions", "generate_risk_report"],
"calls_made": 23,
"call_budget": 100,
"status": "active",
"created_at": "2026-03-15T14:00:00Z"
}
Close Session¶
DELETE /sessions/{id}
Marks the session as Closed. Subsequent requests with this session ID return 408.
Response (200):
{
"status": "closed"
}
Policy Management¶
Explain (Dry-Run)¶
POST /policy/explain
Evaluates a hypothetical request against loaded policies without actually proxying anything.
Request Body:
{
"agent_id": "550e8400-...",
"trust_level": "basic",
"capabilities": ["read"],
"declared_intent": "read configuration files",
"tool_name": "read_file",
"principal_sub": "user:alice",
"principal_groups": ["dev-team"]
}
Response (200):
{
"decision": "allow",
"matched_policy": "allow-read-basic",
"trace": []
}
Validate Policy TOML¶
POST /policy/validate
Request Body:
{
"toml": "[[policies]]\nid = \"test\"\neffect = \"allow\""
}
Response (200):
{
"valid": true,
"policies_count": 1,
"errors": []
}
Reload Policies¶
POST /policy/reload
Forces an immediate reload from the configured policy file. Returns the new policy count or errors.
Get Policy Schema¶
GET /policy/schema
Returns the expected TOML schema for policy files.
Error Responses¶
All error responses follow a consistent format:
{
"error": "ErrorCode",
"message": "Human-readable explanation"
}
Status |
Code |
When |
|---|---|---|
400 |
|
Invalid request body |
400 |
|
Delegation attempts to widen scope |
401 |
|
Missing or invalid |
404 |
|
Agent or session not found |
429 |
|
Per-agent session cap exceeded |
500 |
|
Server-side failure |