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

owner

string

yes

Human principal (OAuth subject)

model

string

yes

LLM model identifier

capabilities

string[]

yes

Agent capabilities

trust_level

string

yes

untrusted, basic, verified, or trusted

expires_at

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

to

UUID

yes

Target agent ID

scopes

string[]

yes

Capabilities to delegate (must be subset of parent’s)

expires_at

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

agent_id

UUID

yes

Agent this session belongs to

declared_intent

string

yes

Free-form intent description

authorized_tools

string[]

yes

Tool whitelist

time_limit_secs

integer

no

Session duration (default from config)

call_budget

integer

no

Max tool calls (default from config)

rate_limit_per_minute

integer

no

Per-minute rate cap

data_sensitivity

string

no

public, internal, confidential, restricted

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

BadRequest

Invalid request body

400

ScopeNarrowingViolation

Delegation attempts to widen scope

401

Unauthorized

Missing or invalid x-api-key

404

NotFound

Agent or session not found

429

TooManySessions

Per-agent session cap exceeded

500

InternalError

Server-side failure