Skip to content

Latest commit

 

History

History
3973 lines (2843 loc) · 92.4 KB

File metadata and controls

3973 lines (2843 loc) · 92.4 KB

API Reference

Aegis exposes a REST API on http://localhost:9100 (configurable via AEGIS_PORT). All endpoints return JSON unless otherwise noted.

Authentication

Set AEGIS_AUTH_TOKEN to enable bearer token authentication. All endpoints except /v1/health require the Authorization: Bearer <token> header when auth is enabled.

# With auth
curl -H "Authorization: Bearer $TOKEN" http://localhost:9100/v1/sessions

# Without auth (default)
curl http://localhost:9100/v1/sessions

For multi-key auth, see Enterprise Deployment.


1. System

Health, swarm coordination, diagnostics, handshake, and the OpenAPI spec.

Health Check

GET /v1/health

Returns server health, version, uptime, and Claude CLI status.

Role Required Notes
None Unauthenticated callers receive only { "status": "ok" } to prevent information leakage
admin Yes Full response with version, uptime, sessions, Claude CLI status
curl http://localhost:9100/v1/health

Response (authenticated):

{
  "status": "ok",
  "timestamp": "2026-04-08T09:00:00.000Z",
  "version": "0.5.3-preview",
  "platform": "linux",
  "uptime": 3600,
  "sessions": { "active": 3, "total": 42 },
  "claude": {
    "available": true,
    "healthy": true,
    "version": "1.0.18",
    "minimumVersion": "1.0.10",
    "error": null
  }
}

Response (unauthenticated):

{
  "status": "ok"
}

Errors: None (always returns 200). Returns draining status during graceful shutdown.


Version Discovery

GET /v1/version

Returns the server package name and version. No authentication required — this is a public endpoint for service discovery and monitoring. The response also includes an X-Aegis-Version header.

curl http://localhost:9100/v1/version

Response:

{
  "name": "@onestepat4time/aegis",
  "version": "0.6.6"
}

Headers:

Header Value
X-Aegis-Version Server package version (e.g. 0.6.6)

Use cases:

  • Load balancer health checks that need version info
  • CI/CD pipelines verifying deployed version
  • Monitoring dashboards tracking fleet versions

Swarm Status

GET /v1/swarm

Returns the status of parallel session swarm coordination.

Role Required
admin, operator, viewer Yes
curl http://localhost:9100/v1/swarm \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "swarms": []
}

Diagnostics

GET /v1/diagnostics

Returns bounded, no-PII diagnostic events from the internal diagnostics bus.

Role Required
admin, operator Yes

Query parameters:

Parameter Type Default Description
limit integer 50 Max events to return (1–100)
curl "http://localhost:9100/v1/diagnostics?limit=20" \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "count": 2,
  "events": [
    { "type": "session.create", "ts": "2026-04-13T10:00:00.000Z", "detail": "..." }
  ]
}

Client Handshake

POST /v1/handshake

Performs capability negotiation with Aegis. Returns server capabilities and compatibility status. No authentication required.

curl -X POST http://localhost:9100/v1/handshake \
  -H "Content-Type: application/json" \
  -d '{"clientVersion": "1.2.3", "capabilities": ["streamable-events", "tool-use"]}'

Request body:

Parameter Type Required Description
clientVersion string yes Client semantic version string
capabilities string[] yes Array of client capability names

Response (200 OK):

{
  "compatible": true,
  "serverVersion": "0.5.3-preview",
  "capabilities": ["streamable-events", "tool-use", "permission-requests"]
}

Response (409 Conflict): Returned when protocol versions are incompatible.


OpenAPI Spec

GET /v1/openapi.json

Returns the machine-readable OpenAPI 3.1 specification for all endpoints.

curl http://localhost:9100/v1/openapi.json

Response: OpenAPI 3.1 JSON document.


Prometheus Metrics

GET /metrics

Prometheus-compatible metrics scrape endpoint (standard path, no /v1 prefix).

Role Required
admin Yes
curl http://localhost:9100/metrics \
  -H "Authorization: Bearer $TOKEN"

Response: Prometheus text format (Content-Type: text/plain; version=0.0.4).


API v2 Migration Stub

GET /v2/

Returns versioning metadata for the planned v2 API. No v2 endpoints exist yet.

curl http://localhost:9100/v2/

Response:

{
  "version": 2,
  "status": "planned",
  "message": "API v2 is not yet available. Continue using /v1/ endpoints.",
  "migration_guide": "/v1/openapi.json",
  "v1_base": "/v1/"
}

2. Authentication

Token verification, API key management, SSE tokens, and OAuth2 device authorization.

Verify Auth Token

POST /v1/auth/verify

Verifies if the current auth token is valid. Returns token metadata and sets a dashboard session cookie.

curl -X POST http://localhost:9100/v1/auth/verify \
  -H "Content-Type: application/json" \
  -d '{"token": "your-token"}'

Request body:

Parameter Type Required Description
token string yes The auth token to verify

Response (200 OK):

{
  "valid": true,
  "role": "admin"
}

Errors:

Status Condition
401 Invalid token
429 Rate limited

Create API Key

POST /v1/auth/keys

Creates a new API key. Admin only.

Role Required
admin Yes
curl -X POST http://localhost:9100/v1/auth/keys \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "ci-bot", "role": "operator"}'

Request body:

Parameter Type Required Description
name string yes Key name (alphanumeric, dots, hyphens, underscores; max 100 chars)
role string yes One of: admin, operator, viewer
rateLimit number no Requests per minute
ttlDays number no Key expiration in days
permissions string[] no Permission names: create, send, approve, reject, kill
tenantId string no Assign key to a tenant

Response (201 Created):

{
  "id": "key-abc123",
  "key": "ak_...",
  "name": "ci-bot",
  "expiresAt": null,
  "role": "operator",
  "permissions": ["create", "send"]
}

Convenience alias: POST /v1/keys — identical behavior.


List API Keys

GET /v1/auth/keys

Lists all registered API keys (metadata only, no secrets). Admin only.

Role Required
admin Yes
curl http://localhost:9100/v1/auth/keys \
  -H "Authorization: Bearer $TOKEN"

Response: Array of key summary objects with id, name, createdAt, lastUsedAt, rateLimit, expiresAt, role, permissions.

Convenience alias: GET /v1/keys


Delete API Key

DELETE /v1/auth/keys/:id

Revokes an API key by ID. Admin only.

Role Required
admin Yes
curl -X DELETE http://localhost:9100/v1/auth/keys/key-abc123 \
  -H "Authorization: Bearer $TOKEN"

Response: { "ok": true }

Errors:

Status Condition
404 Key not found

Convenience alias: DELETE /v1/keys/:id


Update API Key

PATCH /v1/auth/keys/:id

Updates an API key's role, name, or permissions. Admin only.

Role Required
admin Yes
curl -X PATCH http://localhost:9100/v1/auth/keys/key-abc123 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"role": "operator"}'

Request body:

Parameter Type Required Description
name string no New key name (alphanumeric, dots, hyphens, underscores; max 100 chars; must be unique)
role string no New role: admin, operator, or viewer
permissions string[]|null no Explicit permissions array, or null to reset to role defaults

Behavior:

  • Changing role resets permissions to that role's defaults (unless permissions is also provided).
  • Setting permissions to null resets to role defaults.
  • The last admin cannot demote themselves (returns 409).
  • Key names must be unique (returns 409 on conflict).

Response: Updated key object.

Errors:

Status Condition
404 Key not found
409 Cannot demote last admin, or key name already in use
403 Auth is not enabled

Rotate API Key

POST /v1/auth/keys/:id/rotate

Rotates an API key in place. The old key is immediately invalidated. Admin only.

Role Required
admin Yes
curl -X POST http://localhost:9100/v1/auth/keys/key-abc123/rotate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ttlDays": 365}'

Request body:

Parameter Type Required Description
ttlDays number no New TTL in days

Response: Rotated key object with new key value.

Errors:

Status Condition
404 Key not found

Rotate API Key (Zero-Downtime)

POST /v1/auth/keys/rotate

Rotates an API key with a grace period during which both old and new keys are valid. Admin only.

Role Required
admin Yes
curl -X POST http://localhost:9100/v1/auth/keys/rotate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keyId": "key-abc123", "gracePeriodSeconds": 120, "ttlDays": 365}'

Request body:

Parameter Type Required Description
keyId string yes ID of the key to rotate
gracePeriodSeconds number no How long old key stays valid (default: AEGIS_KEY_ROTATION_GRACE_SECONDS, min: 1, max: 86400)
ttlDays number no New TTL in days

Response:

{
  "keyId": "key-abc123",
  "key": "ak-new-...",
  "expiresAt": "2027-04-23T12:00:00.000Z",
  "graceEndsAt": "2026-04-23T12:02:00.000Z"
}

Create SSE Token

POST /v1/auth/sse-token

Returns a short-lived token (sse_-prefixed) required to authenticate SSE event stream connections. SSE endpoints reject regular Bearer tokens — you must obtain an SSE token first.

curl -X POST http://localhost:9100/v1/auth/sse-token \
  -H "Authorization: Bearer $TOKEN"

Response (201 Created):

{ "token": "sse_3799ebe2daa4a0b6...", "expiresAt": 1778111504485 }
Field Type Description
token string sse_-prefixed token for SSE stream auth
expiresAt number Expiration timestamp (ms since epoch). Tokens are valid for 60 seconds.

Errors:

Status Condition
401 Invalid or expired API key
429 SSE token limit reached (max 10 outstanding per key)

Get Key Quotas

GET /v1/auth/keys/:id/quotas

Returns configured quotas and current usage for a specific API key. Admin only.

Role Required
admin Yes
curl http://localhost:9100/v1/auth/keys/key-abc123/quotas \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "quotas": {
    "maxConcurrentSessions": 10,
    "maxTokensPerWindow": 1000000,
    "maxSpendPerWindow": 50.00,
    "quotaWindowMs": 3600000
  },
  "usage": {
    "activeSessions": 3,
    "tokensInWindow": 245000,
    "spendInWindow": 12.50,
    "windowMs": 3600000
  }
}

Set Key Quotas

PUT /v1/auth/keys/:id/quotas

Sets or updates quotas for an API key. Omit a field to leave unchanged; set to null to remove. Admin only.

Role Required
admin Yes
curl -X PUT http://localhost:9100/v1/auth/keys/key-abc123/quotas \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "maxConcurrentSessions": 10,
    "maxTokensPerWindow": 1000000,
    "maxSpendPerWindow": 50.00
  }'

Request body:

Parameter Type Description
maxConcurrentSessions number | null Max concurrent sessions
maxTokensPerWindow number | null Max tokens per window
maxSpendPerWindow number | null Max spend (USD) per window
quotaWindowMs number Window duration in ms (default: 3600000)

Device Authorization Grant (OAuth2, RFC 8628)

Aegis can act as a device-code broker between the CLI (ag login) and your IdP. Requires AEGIS_OIDC_ISSUER and AEGIS_OIDC_CLIENT_ID.

Initiate Device Flow

POST /v1/auth/device/authorize

Proxies a device authorization request to the IdP.

curl -X POST http://localhost:9100/v1/auth/device/authorize \
  -H "Content-Type: application/json" \
  -d '{"client_id": "your-client-id", "scope": "openid profile email"}'

Request body:

Parameter Type Required Description
client_id string yes Must match AEGIS_OIDC_CLIENT_ID
scope string no Defaults to configured scopes

Response (200 OK): RFC 8628 device authorization response with device_code, user_code, verification_uri, expires_in, interval.

Errors:

Status Condition
400 client_id mismatch
502 IdP unreachable or doesn't support device auth
503 OIDC not configured

Poll for Token

POST /v1/auth/device/token

Polls the IdP for the device-code grant result.

curl -X POST http://localhost:9100/v1/auth/device/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "urn:ietf:params:oauth2:grant-type:device_code",
    "device_code": "GmRhmh...",
    "client_id": "your-client-id"
  }'

Request body:

Parameter Type Required Description
grant_type string yes Must be urn:ietf:params:oauth2:grant-type:device_code
device_code string yes From the authorize response
client_id string yes Must match configured OIDC client

Response (200 OK): Token response with access_token, token_type, expires_in, refresh_token, id_token.

Errors:

Status Error Description
400 authorization_pending User hasn't completed auth yet
400 slow_down Poll too fast — increase interval by 5s
400 expired_token Device code expired
400 access_denied User denied
502 server_error IdP unreachable
503 server_error OIDC not configured

3. Sessions

Session lifecycle: create, list, get, batch operations, history, and statistics.

List Sessions

GET /v1/sessions

Lists active sessions with pagination, status, and project filters. Non-admin keys see only their own sessions.

curl "http://localhost:9100/v1/sessions?page=1&limit=20&status=working" \
  -H "Authorization: Bearer $TOKEN"

Query parameters:

Parameter Type Default Description
page integer 1 Page number (1-based)
limit integer 20 Items per page (max 100)
status string Filter by session status
project string Filter by workDir substring match

Response:

{
  "sessions": [
    { "id": "abc123", "name": "feature-auth", "status": "working", "workDir": "/project", "createdAt": 1712650800000 }
  ],
  "pagination": { "page": 1, "limit": 20, "total": 42, "totalPages": 3 }
}

Create Session

POST /v1/sessions

Creates a new Claude Code session. Reuses an existing idle session for the same workDir if available. Rate limited: 120 req/min.

curl -X POST http://localhost:9100/v1/sessions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "feature-auth",
    "workDir": "/home/user/my-project",
    "model": "claude-sonnet-4-20250514",
    "prompt": "Build a login page with email/password fields.",
    "systemPrompt": "You are a senior frontend developer. Use TypeScript and React."
  }'

Request body:

Parameter Type Required Description
workDir string yes Absolute path to an existing directory (file paths are rejected)
name string no Session name (max 200 chars, a-zA-Z0-9_ ./@-= only; defaults to auto-generated)
label string no Alias for name (same character restrictions; name takes precedence)
prompt string no Initial prompt to send after boot (max 100k chars; must be non-empty if provided). For follow-up messages after creation, use POST /v1/sessions/:id/send with text field.
prd string no Product Requirements Document text (max 100k chars)
resumeSessionId string (UUID) no Resume an existing session by UUID
model string no Model name for analytics grouping (max 200 chars)
claudeCommand string no Custom Claude Code CLI flags (max 500 chars, alphanumeric/safe chars only)
env object no Environment variables (subject to denylist)
stallThresholdMs number no Stall detection timeout (default: 300000, max: 3600000)
permissionMode string no default, bypassPermissions, plan, acceptEdits, dontAsk, auto
autoApprove boolean no Skip permission prompts (= permissionMode: bypassPermissions)
parentId string (UUID) no Set parent session — child appears in parent's /children
memoryKeys string[] no Pre-load memory entries into session (max 50)
systemPrompt string no Per-session custom system prompt passed via ACP _meta.systemPrompt (max 100k chars; ACP only)

Multi-tenancy: Sessions inherit tenantId from the creating API key.

Response (201 Created):

{
  "id": "abc123",
  "name": "feature-auth",
  "workDir": "/home/user/my-project",
  "status": "working",
  "createdAt": 1712650800000,
  "promptDelivery": { "delivered": true, "attempts": 1, "status": "delivered" }
}

promptDelivery fields:

Field Type Description
delivered boolean Whether the prompt was successfully delivered to the agent
attempts number Number of delivery attempts
status string Delivery status: delivered or failed

Synchronous delivery: The server blocks until the agent acknowledges the prompt. promptDelivery.status is delivered or failed immediately in the response — no polling needed.

ACP disabled: When ACP is disabled, delivered is false with no agent process spawned. The response includes a warning field. Enable via AEGIS_ACP_ENABLED=true or "acpEnabled": true in config.

Response (200 OK): Returned when reusing an existing idle session (reused: true).

Errors:

Status Code Condition
400 Invalid request body, missing workDir, file path as workDir, empty prompt, disallowed characters in name/label, env denylist rejection
403 TENANT_WORKDIR_DENIED workDir outside tenant root
422 CC_VERSION_TOO_OLD Claude Code version below minimum
429 QUOTA_EXCEEDED Per-key session quota exceeded

Get Session

GET /v1/sessions/:id

Returns session details including action hints for interactive states.

Role Required
admin, operator, viewer Yes
curl http://localhost:9100/v1/sessions/abc123 \
  -H "Authorization: Bearer $TOKEN"

Response: Session object with actionHints for current interactive state.

Errors:

Status Condition
404 Session not found (or unauthorized — 404 is returned for both to prevent ID enumeration)

Batch Create Sessions

POST /v1/sessions/batch

Creates up to 50 sessions in a single request. Per-key rate limit: 1 batch per 5 seconds. Global cap: 200 concurrent sessions.

curl -X POST http://localhost:9100/v1/sessions/batch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sessions": [
      {"name": "task-1", "workDir": "/project", "prompt": "Task 1"},
      {"name": "task-2", "workDir": "/project", "prompt": "Task 2"}
    ]
  }'

Request body:

Parameter Type Required Description
sessions array yes Array of session specs (max 50, each with workDir)

Response (201 Created): Batch creation result.

Errors:

Status Condition
400 Invalid workDir in one or more specs
429 Batch rate limit or global session cap exceeded

Batch Delete Sessions

DELETE /v1/sessions/batch

Kills and removes sessions by IDs or status filter. At least one of ids or status is required.

curl -X DELETE http://localhost:9100/v1/sessions/batch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids": ["abc123", "def456"]}'

Request body:

Parameter Type Required Description
ids string[] conditional Session UUIDs to kill (max 100)
status string conditional Kill all sessions with this status

Response:

{
  "deleted": 2,
  "notFound": [],
  "errors": []
}

Session History

GET /v1/sessions/history

Returns paginated history of all sessions from the audit log and live state. Non-admin keys see only their own sessions.

curl "http://localhost:9100/v1/sessions/history?page=1&limit=20&status=active" \
  -H "Authorization: Bearer $TOKEN"

Query parameters:

Parameter Type Default Description
page integer 1 Page number
limit integer 50 Items per page (max 200)
status string Filter: any UIState value (e.g. active, pending, killed, unknown)
ownerKeyId string Filter by owner API key
Role Required
admin, operator, viewer Yes

Response:

{
  "records": [
    {
      "id": "3f47a2b1",
      "ownerKeyId": "key-abc123",
      "createdAt": 1744531200000,
      "endedAt": 1744533000000,
      "lastSeenAt": 1744533000000,
      "finalStatus": "killed",
      "source": "audit+live"
    }
  ],
  "pagination": { "page": 1, "limit": 20, "total": 42, "totalPages": 3 }
}

Session Statistics

GET /v1/sessions/stats

Returns aggregated session statistics. Non-admin keys see only their own. The active count excludes terminal sessions (killed, completed, crashed); use byStatus for a full breakdown.

curl http://localhost:9100/v1/sessions/stats \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "active": 3,
  "byStatus": { "working": 2, "idle": 1 },
  "totalCreated": 142,
  "totalCompleted": 87,
  "totalFailed": 12
}

4. Session Observability

Transcript access, read output, metrics, latency, summary, health checks, tool usage, and per-session SSE.

Read Session Output

GET /v1/sessions/:id/read

Reads current session messages and output.

curl http://localhost:9100/v1/sessions/abc123/read \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "id": "abc123",
  "status": "idle",
  "output": "I've created the login page with...",
  "tokenUsage": { "input": 1500, "output": 800 }
}

Session status values: idle, working, compacting, context_warning, waiting_for_input, permission_prompt, plan_mode, ask_question, bash_approval, settings, error, rate_limit, pending, killed, completed, crashed, unknown.

Terminal states: killed (session was stopped via API), completed (session finished normally), crashed (session terminated unexpectedly). Terminal sessions return 404 on kill attempts and are retained for audit.


Paginated Transcript

GET /v1/sessions/:id/transcript

Returns a page-based transcript for a session.

curl "http://localhost:9100/v1/sessions/abc123/transcript?page=1&limit=50&role=user" \
  -H "Authorization: Bearer $TOKEN"

Query parameters:

Parameter Type Default Description
page integer 1 Page number
limit integer 50 Entries per page (max 200)
role string Filter: user, assistant, or system

Response: Paginated transcript entries.

Errors:

Status Condition
400 Invalid role filter
404 Session or transcript not found

Paginated Transcript (Cursor)

GET /v1/sessions/:id/transcript/cursor

Cursor-based transcript navigation. Use before_id for pagination.

curl "http://localhost:9100/v1/sessions/abc123/transcript/cursor?limit=50&role=user&before_id=100" \
  -H "Authorization: Bearer $TOKEN"

Query parameters:

Parameter Type Default Description
limit integer 50 Max entries per page (max 200)
before_id integer Cursor — entry ID to start before
role string Filter: user, assistant, or system

Response:

{
  "entries": [
    { "id": 1, "role": "user", "content": "...", "timestamp": "..." },
    { "id": 2, "role": "assistant", "content": "...", "timestamp": "..." }
  ],
  "hasMore": true
}

Export Session Transcript

GET /v1/sessions/:id/export

Download the full session transcript as JSONL (NDJSON) or Markdown.

# JSONL format (default)
curl "http://localhost:9100/v1/sessions/abc123/export?format=jsonl" \
  -H "Authorization: Bearer $TOKEN" \
  -o session.jsonl

# Markdown format
curl "http://localhost:9100/v1/sessions/abc123/export?format=markdown" \
  -H "Authorization: Bearer $TOKEN" \
  -o session.md

Query parameters:

Parameter Type Default Description
format string jsonl Export format: jsonl or markdown

JSONL response: Each line is a JSON object with the transcript entry:

{"role":"user","contentType":"text","text":"Hello","timestamp":"2026-05-10T15:00:00Z"}
{"role":"assistant","contentType":"text","text":"Hi there!","timestamp":"2026-05-10T15:00:01Z"}

JSONL fields per entry:

Field Description
role Message role (user/assistant)
contentType Content type (text/tool_use/etc.)
text Message content
toolName Tool name (tool_use entries only)
toolUseId Tool use ID (if applicable)
timestamp ISO 8601 timestamp

Markdown response: Readable export with section headers, tool calls in collapsible <details> blocks, thinking blocks (💭), and error callouts (⚠️).

# Session Export: my-session

> Exported: 2026-05-10T15:50:00Z
> Session ID: abc-123

---

### 👤 User
Hello, how are you?

### 🤖 Assistant
I am doing well, thanks!

Errors:

Status Condition
400 Unsupported format (not jsonl/markdown)
404 Session not found or no transcript data

Per-Session Metrics

GET /v1/sessions/:id/metrics

Returns metrics for a specific session (message counts, tool calls, token usage).

curl http://localhost:9100/v1/sessions/abc123/metrics \
  -H "Authorization: Bearer $TOKEN"

Response: Session metrics object.

Errors:

Status Condition
404 No metrics for this session

Per-Session Tool Usage

GET /v1/sessions/:id/tools

Returns per-tool call counts for a session, parsed from the JSONL transcript. Ownership-based access — requires the session's API key owner.

curl http://localhost:9100/v1/sessions/abc123/tools \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "sessionId": "abc123",
  "tools": [
    { "name": "Bash", "category": "shell", "count": 12, "totalTokens": 8421 }
  ],
  "totalCalls": 47
}

Session Latency

GET /v1/sessions/:id/latency

Returns per-operation latency metrics for a session (hook latency, state change detection, permission response, channel delivery).

curl http://localhost:9100/v1/sessions/abc123/latency \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "sessionId": "abc123",
  "realtime": { "hookLatencyMs": 45, "permissionResponseMs": 120 },
  "aggregated": { "avgHookLatencyMs": 50, "p99HookLatencyMs": 200 }
}

Session Summary

GET /v1/sessions/:id/summary

Returns an AI-generated summary of the session parsed from transcript. Requires the session to have a JSONL transcript.

curl http://localhost:9100/v1/sessions/abc123/summary \
  -H "Authorization: Bearer $TOKEN"

Response: 200 OK with summary text. 404 if no summary available.


Session Health

GET /v1/sessions/:id/health

Returns liveness and health data for a specific session.

curl http://localhost:9100/v1/sessions/abc123/health \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "alive": true,
  "windowExists": true,
  "claudeRunning": true,
  "paneCommand": "claude --no-input",
  "status": "working",
  "hasTranscript": true,
  "lastActivity": 1712650800000,
  "lastActivityAgo": 2340,
  "sessionAge": 45230000,
  "details": "Session healthy"
}

Bulk Session Health

GET /v1/sessions/health

Health status for all visible sessions (owned sessions, or all if admin).

Role Required
admin, operator, viewer Yes
curl http://localhost:9100/v1/sessions/health \
  -H "Authorization: Bearer $TOKEN"

Response: Object keyed by session ID, each value is a health object (same shape as per-session health).


Per-Session Events (SSE)

GET /v1/sessions/:id/events

Server-Sent Events stream for session-specific events (state changes, permission requests, verification results).

Alias: GET /v1/sessions/:id/stream — identical behavior.

# Step 1: Get an SSE token
curl -s -X POST http://localhost:9100/v1/auth/sse-token \
  -H "Authorization: Bearer $API_KEY"
# Step 2: Connect to the session stream
curl -N "http://localhost:9100/v1/sessions/abc123/events?token=$SSE_TOKEN"

Authentication: SSE token via query parameter (?token=<sse-token>) or Bearer header (Authorization: Bearer sse_...). Regular API keys are rejected. See Create SSE Token above.

Rate limited: Per-IP and global connection limits apply.

Events: connected, heartbeat, status.*, permission.*, verification.*, subagent_start, subagent_stop, circuit_breaker.

Supports Last-Event-ID header for replay of missed events.


Get Child Sessions

GET /v1/sessions/:id/children

Returns child sessions spawned from this session (via /fork or /spawn).

curl http://localhost:9100/v1/sessions/abc123/children \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "children": [
    { "id": "def456", "windowName": "feature-auth-child", "status": "working", "createdAt": 1712651400000 }
  ]
}

Global Tool Definitions

GET /v1/tools

Lists all available MCP tools with their schemas.

Role Required
admin, operator, viewer Yes
curl http://localhost:9100/v1/tools \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "tools": [
    { "name": "Bash", "category": "shell", "description": "..." }
  ],
  "categories": ["shell", "file"],
  "totalTools": 24
}

5. Session Control

Send messages, interrupt, escape, kill, approve, reject, answer, fork, spawn, slash commands, bash mode, and permissions.

Send Message

POST /v1/sessions/:id/send

Sends a text message to the Claude Code session.

Aliases: POST /v1/sessions/:id/input and POST /v1/sessions/:id/prompt — identical behavior.

Note: Session creation accepts a prompt field for the initial message; for follow-up messages, use /send, /input, or /prompt with the text field.

curl -X POST http://localhost:9100/v1/sessions/abc123/send \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text": "Add form validation."}'

Request body:

Parameter Type Required Description
text string yes Message to send

Response:

{
  "ok": true,
  "delivered": true,
  "attempts": 1
}

Errors:

Status Condition
400 Invalid request body
404 Session not found
429 Per-key token/spend quota exceeded

Interrupt Session

POST /v1/sessions/:id/interrupt

Sends Ctrl+C to the Claude Code session. Useful when Claude is stuck or working on the wrong task.

curl -X POST http://localhost:9100/v1/sessions/abc123/interrupt \
  -H "Authorization: Bearer $TOKEN"

Response: { "ok": true }


Escape Session

POST /v1/sessions/:id/escape

Sends Escape to interrupt the current running command and return to the shell prompt.

curl -X POST http://localhost:9100/v1/sessions/abc123/escape \
  -H "Authorization: Bearer $TOKEN"

Response: { "ok": true }


Kill Session

DELETE /v1/sessions/:id

Terminates the Claude Code process and marks the session as killed. The session record is retained (status = killed) for audit purposes. Killing an already-terminated session (killed, completed, crashed) returns 404.

Aliases: POST /v1/sessions/:id/kill, POST /v1/sessions/:id/terminate, POST /v1/sessions/:id/stop — all identical.

curl -X DELETE http://localhost:9100/v1/sessions/abc123 \
  -H "Authorization: Bearer $TOKEN"

Response: { "ok": true, "status": "killed" }

Status Error
404 Session already terminated — session is in a terminal state (killed, completed, crashed)

Answer Pending Question

POST /v1/sessions/:id/answer

Submits an answer to a pending AskUserQuestion prompt in a Claude Code session.

curl -X POST http://localhost:9100/v1/sessions/abc123/answer \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"questionId": "q-001", "answer": "my-answer"}'

Request body:

Parameter Type Required Description
questionId string yes The question ID to answer
answer string yes The answer string

Response: { "ok": true }

Errors:

Status Condition
400 Missing questionId or answer
409 No pending question matching this questionId

Spawn Child Session

POST /v1/sessions/:id/spawn

Creates a child Claude Code session. The child inherits the parent's working directory unless overridden.

curl -X POST http://localhost:9100/v1/sessions/abc123/spawn \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Review the changes in the parent session."}'

Request body:

Parameter Type Required Description
name string no Child session name
prompt string no Initial prompt
workDir string no Override working directory
permissionMode string no Override permission mode

Response (201 Created): Child session object with promptDelivery.


Fork Session

POST /v1/sessions/:id/fork

Creates a new independent session with the same working directory and context.

curl -X POST http://localhost:9100/v1/sessions/abc123/fork \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "feature-auth-experimental", "prompt": "Try alternative approach"}'

Request body:

Parameter Type Required Description
name string no Fork name
prompt string no Initial prompt for the fork

Response (201 Created): Forked session object with forkedFrom parent ID.


Send Slash Command

POST /v1/sessions/:id/command

Sends a slash command to the Claude Code session. Prefixes with / if not provided.

curl -X POST http://localhost:9100/v1/sessions/abc123/command \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"command": "/clear"}'

Request body:

Parameter Type Required Description
command string yes Slash command to send

Response: { "ok": true }


Bash Mode

POST /v1/sessions/:id/bash

Sends a bash command to the session. Prefixes with ! if not provided.

curl -X POST http://localhost:9100/v1/sessions/abc123/bash \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"command": "npm test"}'

Request body:

Parameter Type Required Description
command string yes Bash command to execute

Response: { "ok": true, "output": undefined } (output capture not available in ACP mode).


Quality Gate Verification

POST /v1/sessions/:id/verify

Runs the configured verification protocol on a completed session (test suite, lint, build).

curl -X POST http://localhost:9100/v1/sessions/abc123/verify \
  -H "Authorization: Bearer $TOKEN"

Response:

Status Condition
200 Verification passed
422 Verification failed
404 Session not found
500 Verification error

Get Permission Policy

GET /v1/sessions/:id/permissions

Returns the current permission policy for a session.

curl http://localhost:9100/v1/sessions/abc123/permissions \
  -H "Authorization: Bearer $TOKEN"

Response: { "permissionPolicy": [...] }


Update Permission Policy

PUT /v1/sessions/:id/permissions

Sets the permission policy for a session.

curl -X PUT http://localhost:9100/v1/sessions/abc123/permissions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"permissionPolicy": [{"allowRead": true, "allowWrite": true, "allowBash": false}]}'

Request body: Array of permission rule objects.

Response: { "permissionPolicy": [...] }


Get Permission Profile

GET /v1/sessions/:id/permission-profile

Returns the current permission profile for a session.

curl http://localhost:9100/v1/sessions/abc123/permission-profile \
  -H "Authorization: Bearer $TOKEN"

Response: { "permissionProfile": { ... } } or { "permissionProfile": null }.


Update Permission Profile

PUT /v1/sessions/:id/permission-profile

Sets the permission profile for a session.

curl -X PUT http://localhost:9100/v1/sessions/abc123/permission-profile \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"profile": "restricted"}'

Request body: Permission profile object (validated against permissionProfileSchema).

Response: Updated permission profile.


ACP Control Actions (Pause/Resume/Intervention)

Session pause, resume, and operator intervention for multi-agent control flows.

Pause Session

POST /v1/sessions/:id/pause

Pauses an active session. Queued actions are held; event ingestion continues.

curl -X POST http://localhost:9100/v1/sessions/abc123/pause \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Manual review needed"}'

Request body:

Parameter Type Required Description
reason string no Reason for pause
requestedBy string no Who requested the pause
idempotencyKey string no Deduplication key
metadata object no Arbitrary metadata

Response: Session and pause record.

Errors:

Status Condition
409 Already paused
501 Pause store not configured

Start Intervention

POST /v1/sessions/:id/intervention/start

Starts an intervention on a paused session.

curl -X POST http://localhost:9100/v1/sessions/abc123/intervention/start \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"interventionBy": "operator-1"}'

Request body:

Parameter Type Required Description
interventionBy string no Identifier of the intervening operator

Response: Session and intervention record.

Errors:

Status Condition
409 No active pause

Complete Intervention

POST /v1/sessions/:id/intervention/complete

Completes an active intervention, optionally providing guidance.

curl -X POST http://localhost:9100/v1/sessions/abc123/intervention/complete \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"completedBy": "operator-1", "guidance": "Use the updated API"}'

Request body:

Parameter Type Required Description
completedBy string no Who completed the intervention
guidance string no Guidance text for session resume

Response: Updated pause/intervention record.

Errors:

Status Condition
409 No active intervention

Resume Session

POST /v1/sessions/:id/resume

Resumes a paused or intervening session.

curl -X POST http://localhost:9100/v1/sessions/abc123/resume \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"resumedBy": "operator-1"}'

Request body:

Parameter Type Required Description
resumedBy string no Who resumed the session
resumeMetadata object no Arbitrary metadata

Response: Session and pause record.

Errors:

Status Condition
409 Session is not paused

Get Intervention Status

GET /v1/sessions/:id/intervention

Returns the active or most recent intervention record for a session.

curl http://localhost:9100/v1/sessions/abc123/intervention \
  -H "Authorization: Bearer $TOKEN"

Response: Serialized intervention record.

Errors:

Status Condition
404 No intervention found
501 Pause store not configured

Cancel Session

POST /v1/sessions/:id/cancel

Cancels a running ACP session. Sends a cancel signal to the ACP backend.

curl -X POST http://localhost:9100/v1/sessions/abc123/cancel \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

Request body:

Parameter Type Required Description
force boolean no Force cancel even if session is busy

Response: { "ok": true }

Errors:

Status Condition
501 ACP backend not configured

Approve Permission

POST /v1/sessions/:id/approval/approve

Approves a pending permission prompt (tool execution, file access, etc.).

curl -X POST http://localhost:9100/v1/sessions/abc123/approval/approve \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"approvalId": "perm-123"}'

Request body:

Parameter Type Required Description
approvalId string yes ID of the pending approval (max 256 chars)
reason string no Reason for approval (max 2048 chars)

Response: Approval result from the ACP backend.

Errors:

Status Condition
500 No pending permission request for the given approval ID
501 ACP backend not configured

Reject Permission

POST /v1/sessions/:id/approval/reject

Rejects a pending permission prompt.

curl -X POST http://localhost:9100/v1/sessions/abc123/approval/reject \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"approvalId": "perm-123", "reason": "Unsafe command"}'

Request body:

Parameter Type Required Description
approvalId string yes ID of the pending approval (max 256 chars)
reason string no Reason for rejection (max 2048 chars)

Response: Rejection result from the ACP backend.

Errors:

Status Condition
500 No pending permission request for the given approval ID
501 ACP backend not configured

Get Pending Approvals

GET /v1/sessions/:id/approval/pending

Returns the current pending permission approval, if any.

curl http://localhost:9100/v1/sessions/abc123/approval/pending \
  -H "Authorization: Bearer $TOKEN"

Response: { "pending": { ... } | null }


6. Session ACP

Claude Code hook callbacks and session-scoped operations.

Permission Hook

POST /v1/sessions/:id/hooks/permission

Called by Claude Code when a permission prompt occurs. The server routes this to the appropriate permission approver.

curl -X POST http://localhost:9100/v1/sessions/abc123/hooks/permission \
  -H "Content-Type: application/json" \
  -d '{"tool_name": "Bash", "tool_input": {"command": "rm -rf /"}, "permission_mode": "default"}'

Request body:

Parameter Type Required Description
tool_name string no Tool requesting permission
tool_input object no Tool input parameters
permission_mode string no Permission mode

Response: 200 OK with empty body. 404 if session not found.


Stop Hook

POST /v1/sessions/:id/hooks/stop

Called when a session stops externally. Cleans up session state.

curl -X POST http://localhost:9100/v1/sessions/abc123/hooks/stop \
  -H "Content-Type: application/json" \
  -d '{"stop_reason": "user_interrupt"}'

Request body:

Parameter Type Required Description
stop_reason string no Reason for stop

Response: 200 OK with empty body. 404 if session not found.


Screenshot

POST /v1/sessions/:id/screenshot

Captures a screenshot of a URL using Playwright. Requires Playwright installed.

curl -X POST http://localhost:9100/v1/sessions/abc123/screenshot \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "http://localhost:3000", "fullPage": true}'

Request body:

Parameter Type Required Description
url string yes URL to screenshot
fullPage boolean no Capture full page
width number no Viewport width
height number no Viewport height

Response: { "image": "<base64>", "width": 1200, "height": 800 }

Errors:

Status Condition
400 Invalid URL or SSRF risk
501 Playwright not installed

Session-Scoped Memories

POST /v1/sessions/:id/memories
GET  /v1/sessions/:id/memories

Write and read memory entries scoped to a session.

# Write
curl -X POST http://localhost:9100/v1/sessions/abc123/memories \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"key": "task-status", "value": "in-progress"}'

# Read
curl http://localhost:9100/v1/sessions/abc123/memories \
  -H "Authorization: Bearer $TOKEN"

Write request body:

Parameter Type Required Description
key string yes Memory key (max 200 chars)
value string yes Memory value (max 100 KB)
ttlSeconds number no TTL in seconds (max 30 days)

Read response: { "sessionId": "abc123", "entries": [...] }


ACP Event Replay

Get Session Events

GET /v1/sessions/:id/events

Retrieves stored ACP event stream for a session. (This is the SSE endpoint documented above in Section 4.)

Replay Events

POST /v1/sessions/:id/events/replay

Replays events to restore terminal or UI state. Placeholder implementation.

curl -X POST http://localhost:9100/v1/sessions/abc123/events/replay \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"fromSeq": 0, "toSeq": 100}'

Request body:

Parameter Type Required Description
fromSeq number no Start sequence
toSeq number no End sequence

Response: { "replayed": 0, "restored": false }

Get Event Schema

GET /v1/sessions/:id/events/schema

Returns the ACP event schema with supported event types and field definitions.

curl http://localhost:9100/v1/sessions/abc123/events/schema \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "version": "1.0",
  "eventTypes": ["session.created", "message.sent", "permission.requested", "..."],
  "fields": { "sessionId": { "type": "string", "description": "..." }, "..." }
}

Driver Controls

Manage driver (operator) ownership of ACP sessions. The driver has exclusive send/control access; other connections are observers.

Claim Driver

POST /v1/sessions/:id/driver/claim

Claims driver ownership of a session. Only one driver can be active at a time.

Role Required
admin, operator (send) Yes
curl -X POST http://localhost:9100/v1/sessions/abc123/driver/claim \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"holderId": "operator-1", "ttlMs": 3600000}'

Request body:

Parameter Type Required Description
holderId string no Unique holder identifier (max 256 chars). Defaults to the auth key ID.
ttlMs number no Claim TTL in milliseconds (max 3,600,000 = 1 hour)

Response: Claim result from the ACP backend.

Errors:

Status Condition
409 Driver already claimed (DRIVER_CLAIMED)
501 ACP backend not configured

Release Driver

POST /v1/sessions/:id/driver/release

Releases driver ownership, allowing another operator to claim it.

Role Required
admin, operator (send) Yes
curl -X POST http://localhost:9100/v1/sessions/abc123/driver/release \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"holderId": "operator-1"}'

Request body:

Parameter Type Required Description
holderId string no Holder to release (max 256 chars). Defaults to the auth key ID.

Response: Release result from the ACP backend.

Errors:

Status Condition
501 ACP backend not configured

Transfer Driver

POST /v1/sessions/:id/driver/transfer

Transfers driver ownership to another subscriber.

Role Required
admin, operator (send) Yes
curl -X POST http://localhost:9100/v1/sessions/abc123/driver/transfer \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"targetSubscriberId": "operator-2", "reason": "Shift change"}'

Request body:

Parameter Type Required Description
targetSubscriberId string yes Subscriber to transfer to (max 256 chars)
reason string no Reason for transfer (max 2048 chars)

Response: Transfer result from the ACP backend.

Errors:

Status Condition
501 ACP backend not configured

Get Participants

GET /v1/sessions/:id/participants

Returns the current driver and observer list for a session.

curl http://localhost:9100/v1/sessions/abc123/participants \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "driver": { "holderId": "operator-1", "claimedAt": "2026-05-07T00:00:00Z" },
  "observers": [],
  "activeCount": 1
}

When ACP backend is not configured: { "driver": null, "observers": [], "activeCount": 0 }


Terminal REST API

ACP terminal debug endpoints for programmatic terminal access (open, input, resize, reconnect, close). These complement the WebSocket terminal streaming documented in the WebSocket section below.

Get Terminal Content

GET /v1/sessions/:id/terminal/content

Returns a snapshot of terminal content for a session. When a terminalId query param is provided and the ACP terminal bridge is active, returns the current terminal output. Otherwise returns a graceful empty response.

curl http://localhost:9100/v1/sessions/abc123/terminal/content?terminalId=term-1 \
  -H "Authorization: Bearer $TOKEN"

Query parameters:

Parameter Type Required Description
terminalId string no Terminal session ID. If omitted, returns empty content.

Response:

{
  "content": "...terminal output...",
  "terminalId": "term-1",
  "columns": 120,
  "rows": 40,
  "source": "terminal_bridge"
}

When no terminal bridge or no terminalId:

{
  "content": "",
  "source": "unavailable"
}

Errors:

Status Condition
404 Session not found

Open Terminal

POST /v1/sessions/:id/terminal/open

Opens a new terminal session for an ACP session.

Role Required
admin, operator (send) Yes
curl -X POST http://localhost:9100/v1/sessions/abc123/terminal/open \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

Response: Terminal open result (contains terminalId).

Errors:

Status Condition
501 ACP terminal extension not supported (TERMINAL_UNSUPPORTED)
503 ACP runtime not active (RUNTIME_UNAVAILABLE)

Send Terminal Input

POST /v1/sessions/:id/terminal/input

Sends raw input data to an open terminal.

Role Required
admin, operator (send) Yes
curl -X POST http://localhost:9100/v1/sessions/abc123/terminal/input \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"terminalId": "term-1", "data": "ls -la\n"}'

Request body:

Parameter Type Required Description
terminalId string yes Terminal session ID
data string yes Raw input data (max 4096 chars)

Response: { "ok": true }

Errors:

Status Condition
501 ACP terminal bridge not configured

Resize Terminal

POST /v1/sessions/:id/terminal/resize

Resizes the terminal viewport.

Role Required
admin, operator (send) Yes
curl -X POST http://localhost:9100/v1/sessions/abc123/terminal/resize \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"terminalId": "term-1", "columns": 120, "rows": 40}'

Request body:

Parameter Type Required Description
terminalId string yes Terminal session ID
columns number yes Column count (1–512)
rows number yes Row count (1–512)

Response: { "ok": true }

Errors:

Status Condition
501 ACP terminal bridge not configured

Reconnect Terminal

POST /v1/sessions/:id/terminal/reconnect

Reconnects to an existing terminal session (e.g., after connection drop).

Role Required
admin, operator (send) Yes
curl -X POST http://localhost:9100/v1/sessions/abc123/terminal/reconnect \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"terminalId": "term-1"}'

Request body:

Parameter Type Required Description
terminalId string yes Terminal session ID to reconnect

Response: Reconnection result with current terminal state.

Errors:

Status Condition
501 ACP terminal bridge not configured

Close Terminal

POST /v1/sessions/:id/terminal/close

Closes an open terminal session.

Role Required
admin, operator (send) Yes
curl -X POST http://localhost:9100/v1/sessions/abc123/terminal/close \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"terminalId": "term-1"}'

Request body:

Parameter Type Required Description
terminalId string yes Terminal session ID to close

Response: { "ok": true }

Errors:

Status Condition
501 ACP terminal bridge not configured

7. Analytics

Cost breakdowns, token usage, and rate-limit analytics.

Get Analytics Summary

GET /v1/analytics/summary

Returns aggregated session, token, cost, duration, and error-rate data from the MetricsCache.

Role Required
admin, operator, viewer Yes (viewer blocked for non-system tenants)
curl http://localhost:9100/v1/analytics/summary \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "sessionVolume": [{ "date": "2026-04-22", "created": 42 }],
  "tokenUsageByModel": [
    { "model": "claude-sonnet-4-20250514", "inputTokens": 2400000, "outputTokens": 800000, "cacheCreationTokens": 100000, "cacheReadTokens": 500000, "estimatedCostUsd": 52.40 }
  ],
  "costTrends": [{ "date": "2026-04-22", "cost": 24.50, "sessions": 42 }],
  "topApiKeys": [{ "keyId": "ak_abc123", "keyName": "ci-bot", "sessions": 30, "messages": 500, "estimatedCostUsd": 18.20 }],
  "durationTrends": [{ "date": "2026-04-22", "avgDurationSec": 245, "count": 38 }],
  "errorRates": { "totalSessions": 500, "failedSessions": 12, "failureRate": 0.024, "infraFailures": 57, "adjustedFailureRate": 0.028, "permissionPrompts": 85, "approvals": 72, "autoApprovals": 45 },
  "generatedAt": "2026-04-22T12:00:00.000Z"
}
Field Description
errorRates.infraFailures Sessions that failed without any CC activity (e.g., tmux downtime, zombie sessions). Not counted in adjustedFailureRate.
errorRates.adjustedFailureRate Failure rate excluding infrastructure failures: (failedSessions - infraFailures) / (totalSessions - infraFailures).

Get Cost Breakdown

GET /v1/analytics/costs

Returns aggregated cost breakdown by model, key, and daily trends.

Role Required
admin, operator, viewer Yes (viewer blocked for non-system tenants)
curl http://localhost:9100/v1/analytics/costs \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "totalCostUsd": 152.38,
  "totalSessions": 87,
  "byModel": [
    { "model": "claude-sonnet-4-20250514", "inputTokens": 3200000, "outputTokens": 1100000, "cacheCreationTokens": 150000, "cacheReadTokens": 600000, "estimatedCostUsd": 98.50, "sessions": 52 }
  ],
  "byKey": [
    { "keyId": "ak_abc123", "keyName": "ci-bot", "estimatedCostUsd": 72.30, "sessions": 35, "messages": 500 }
  ],
  "dailyTrends": [
    { "date": "2026-04-22", "estimatedCostUsd": 24.50, "sessions": 12 }
  ],
  "generatedAt": "2026-04-28T06:00:00.000Z"
}

Get Token Usage

GET /v1/analytics/tokens

Returns aggregated token usage with per-model distribution, daily cost trends, and per-day token breakdown.

Query Parameters:

Parameter Type Description
from ISO 8601 string Optional. Filter records from this timestamp.
to ISO 8601 string Optional. Filter records up to this timestamp.
Role Required
admin, operator, viewer Yes (viewer blocked for non-system tenants)
# Full breakdown
curl http://localhost:9100/v1/analytics/tokens \
  -H "Authorization: Bearer $TOKEN"

# Filtered by date range
curl "http://localhost:9100/v1/analytics/tokens?from=2026-05-01T00:00:00Z&to=2026-05-10T23:59:59Z" \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "totalTokens": 7500000,
  "totalCostUsd": 152.38,
  "modelDistribution": [
    { "model": "claude-sonnet-4-20250514", "inputTokens": 3200000, "outputTokens": 1100000, "cacheCreationTokens": 150000, "cacheReadTokens": 600000, "estimatedCostUsd": 98.50 }
  ],
  "dailyCost": [
    { "date": "2026-04-22", "estimatedCostUsd": 24.50, "sessions": 12 }
  ],
  "dailyBreakdown": [
    { "date": "2026-05-10", "inputTokens": 300000, "outputTokens": 150000, "cacheReadTokens": 60000, "cacheWriteTokens": 40000 },
    { "date": "2026-05-11", "inputTokens": 280000, "outputTokens": 120000, "cacheReadTokens": 45000, "cacheWriteTokens": 35000 }
  ],
  "generatedAt": "2026-04-28T06:00:00.000Z"
}

Get Rate-Limit Analytics

GET /v1/analytics/rate-limits

Returns per-key quota usage, global rate-limit config, and a session forecast.

Role Required
admin, operator, viewer Yes (viewer sees only own tenant's keys)
curl http://localhost:9100/v1/analytics/rate-limits \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "global": { "max": 600, "timeWindowMs": 60000 },
  "perKey": [
    { "keyId": "ak_abc123", "keyName": "ci-bot", "activeSessions": 3, "maxSessions": 10, "tokensInWindow": 450000, "maxTokens": 1000000, "spendInWindowUsd": 8.50, "maxSpendUsd": 50.00, "windowMs": 3600000 }
  ],
  "forecast": { "estimatedSessionsRemaining": 7, "bottleneck": "concurrent_sessions" },
  "generatedAt": "2026-04-28T06:00:00.000Z"
}

Get Session Cost

GET /v1/sessions/:id/cost

Returns per-session cost summary with burn rate, cache-hit rate, and token breakdown.

Role Required
admin, operator, viewer (owner) Yes
curl http://localhost:9100/v1/sessions/sess_abc123/cost \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "sessionId": "sess_abc123",
  "totalInputTokens": 150000,
  "totalOutputTokens": 50000,
  "totalCacheCreationTokens": 20000,
  "totalCacheReadTokens": 80000,
  "cacheHitRate": 0.8,
  "estimatedCostUsd": 3.42,
  "model": "claude-sonnet-4-20250514",
  "burnRateUsdPerHour": 12.5,
  "durationMinutes": 16,
  "recordCount": 24
}

Get Cost Summary

GET /v1/cost/summary

Returns aggregate cost summary with burn rate across all sessions.

Role Required
admin, operator, viewer Yes

Query Parameters:

Parameter Type Required Description
from string no ISO timestamp lower bound (inclusive)
to string no ISO timestamp upper bound (inclusive)
curl "http://localhost:9100/v1/cost/summary?from=2026-05-01T00:00:00Z&to=2026-05-13T00:00:00Z" \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "from": "2026-05-01T00:00:00.000Z",
  "to": "2026-05-13T00:00:00.000Z",
  "totalInputTokens": 2400000,
  "totalOutputTokens": 800000,
  "totalCacheCreationTokens": 100000,
  "totalCacheReadTokens": 500000,
  "cacheHitRate": 0.8333,
  "estimatedCostUsd": 52.40,
  "burnRateUsdPerHour": 4.37,
  "sessions": 42
}

Get Cost by Model

GET /v1/cost/by-model

Returns cost grouped by model with per-model token breakdown and cache-hit rate.

Role Required
admin, operator, viewer Yes

Query Parameters:

Parameter Type Required Description
from string no ISO timestamp lower bound (inclusive)
to string no ISO timestamp upper bound (inclusive)
curl "http://localhost:9100/v1/cost/by-model" \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "from": null,
  "to": null,
  "models": [
    {
      "model": "claude-sonnet-4-20250514",
      "inputTokens": 2000000,
      "outputTokens": 700000,
      "cacheCreationTokens": 80000,
      "cacheReadTokens": 400000,
      "estimatedCostUsd": 45.20,
      "cacheHitRate": 0.8333
    },
    {
      "model": "claude-opus-4-20250514",
      "inputTokens": 400000,
      "outputTokens": 100000,
      "cacheCreationTokens": 20000,
      "cacheReadTokens": 100000,
      "estimatedCostUsd": 7.20,
      "cacheHitRate": 0.8333
    }
  ],
  "totalModels": 2,
  "totalCostUsd": 52.40
}

8. Monitoring

Prometheus metrics, aggregated metrics, alert management, and channel health.

Global Metrics

GET /v1/metrics

Returns token usage tracking and cost estimation across all sessions.

Role Required
admin, operator Yes
curl http://localhost:9100/v1/metrics \
  -H "Authorization: Bearer $TOKEN"

Response: Global metrics object with session counts, token totals, and cost estimates.

{
  "uptime": 86400,
  "sessions": {
    "total_created": 150,
    "currently_active": 3,
    "completed": 120,
    "failed": 12,
    "infra_failed": 5,
    "avg_duration_sec": 245,
    "avg_messages_per_session": 18.3
  },
  "auto_approvals": 45,
  "webhooks_sent": 200,
  "webhooks_failed": 1,
  "screenshots_taken": 0,
  "pipelines_created": 2,
  "batches_created": 1,
  "prompt_delivery": { "sent": 500, "delivered": 498, "failed": 2 },
  "token_usage": { "totalInputTokens": 5000000, "totalOutputTokens": 1200000, "totalCacheCreationTokens": 800000, "totalCacheReadTokens": 400000, "estimatedCostUsd": 98.50 }
}
Field Description
sessions.infra_failed Sessions that failed without any Claude Code activity (infrastructure failure, not code failure). Excluded from adjustedFailureRate in analytics.

Aggregated Metrics

GET /v1/metrics/aggregate

Returns aggregated metrics with time-range filtering and grouping. Rate limited: 60 req/min.

Role Required
admin, operator Yes (viewer gets 403)
curl "http://localhost:9100/v1/metrics/aggregate?from=2026-04-01T00:00:00Z&to=2026-04-28T23:59:59Z&groupBy=day" \
  -H "Authorization: Bearer $TOKEN"

Query parameters:

Parameter Type Default Description
from ISO 8601 7 days ago Start timestamp
to ISO 8601 now End timestamp
groupBy string day Grouping: day, hour, or key

Response: Aggregated metrics by time period or API key.


Test Alert Webhook

POST /v1/alerts/test

Tests webhook delivery. Returns delivered: true if the webhook responds with 2xx.

Role Required
admin, operator Yes
curl -X POST http://localhost:9100/v1/alerts/test \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"webhookUrl": "https://example.com/alerts", "secret": "test-secret"}'

Response:

{
  "success": true,
  "delivered": true
}

Errors:

Status Condition
502 Alert delivery failed

Get Alert Statistics

GET /v1/alerts/stats

Returns alert counts for the last 24 hours and totals.

Role Required
admin, operator, viewer Yes
curl http://localhost:9100/v1/alerts/stats \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "last24h": { "sessionFailures": 0, "deadSessions": 0, "processCrashes": 0 },
  "totals": { "sessionFailures": 2, "deadSessions": 1, "processCrashes": 0 }
}

Channel Health

GET /v1/channels/health

Returns health status for all connected channels (Telegram, Slack, Email, webhooks).

Role Required
admin, operator Yes
curl http://localhost:9100/v1/channels/health \
  -H "Authorization: Bearer $TOKEN"

Response: Array of channel health objects:

[
  { "channel": "webhook", "healthy": true, "lastSuccess": "2026-04-22T10:00:00Z", "lastError": null, "pendingCount": 0 }
]

9. Audit

Immutable, hash-chained audit log with CSV/NDJSON export.

Audit Log

GET /v1/audit

Returns audit log records with cursor-based or offset-based pagination, time-range filters, and multi-format export. Admin only. Rate limited: 30 req/min.

Role Required
admin Yes
curl "http://localhost:9100/v1/audit?action=session.create&from=2026-04-13T00:00:00Z&limit=50" \
  -H "Authorization: Bearer $TOKEN"

Query parameters:

Parameter Type Default Description
actor string Filter by actor label (backward compat)
actorKeyId string Filter by actor key ID (takes precedence over actor)
action string Filter by action (e.g. session.create, session.kill)
sessionId string Filter by session ID
from ISO 8601 Inclusive lower timestamp bound
to ISO 8601 Inclusive upper timestamp bound
cursor string Pagination cursor (64-char hex hash)
offset integer Offset (triggers export record format)
limit integer 100 Max records (max 1000)
reverse boolean false Return newest first
verify boolean false Include chain verification metadata
format string json json, csv, or ndjson

JSON response:

{
  "count": 1,
  "total": 1,
  "records": [
    {
      "ts": "2026-04-13T10:00:00.000Z",
      "actor": "key-abc",
      "action": "session.create",
      "sessionId": "abc123",
      "detail": "Created session",
      "prevHash": "",
      "hash": "..."
    }
  ],
  "filters": { "actor": null, "action": "session.create", "sessionId": null, "from": "2026-04-13T00:00:00Z", "to": null },
  "pagination": { "limit": 50, "hasMore": false, "nextCursor": null, "reverse": false },
  "chain": { "count": 1, "firstHash": "...", "lastHash": "...", "badgeHash": "", "firstTs": "...", "lastTs": "..." }
}

CSV/NDJSON export: Set format=csv or format=ndjson. Response includes chain metadata in headers (X-Aegis-Audit-First-Hash, X-Aegis-Audit-Last-Hash, X-Aegis-Audit-Chain-Badge).

Errors:

Status Condition
400 Invalid cursor, bad date format, or from > to
503 Audit logger not enabled

10. Pipelines & Templates

Multi-stage session pipelines and reusable session templates.

Create Pipeline

POST /v1/pipelines

Creates a multi-stage pipeline that runs sessions sequentially.

curl -X POST http://localhost:9100/v1/pipelines \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "build-and-test",
    "workDir": "/home/user/my-project",
    "stages": [
      {"name": "build", "prompt": "Build the project"},
      {"name": "test", "prompt": "Run tests and fix failures"}
    ]
  }'

Request body:

Parameter Type Required Description
name string yes Pipeline name
workDir string yes Working directory
stages array yes Array of stage objects with name/prompt and optional workDir

Response (201 Created): Pipeline object with ID and status.

State Persistence: Pipeline runs are persisted to the StateStore and survive Aegis restarts.


List Pipelines

GET /v1/pipelines

Lists all registered pipelines.

curl http://localhost:9100/v1/pipelines \
  -H "Authorization: Bearer $TOKEN"

Response: Array of pipeline objects.


Get Pipeline

GET /v1/pipelines/:id

Returns status and details of a specific pipeline.

curl http://localhost:9100/v1/pipelines/pipe-123 \
  -H "Authorization: Bearer $TOKEN"

Response: Pipeline object with stage statuses.

Errors:

Status Condition
404 Pipeline not found

List Templates

GET /v1/templates

Lists all registered session templates.

Role Required
admin, operator, viewer Yes
curl http://localhost:9100/v1/templates \
  -H "Authorization: Bearer $TOKEN"

Response: Array of template objects.


Create Template

POST /v1/templates

Creates a reusable session template with optional {{variable}} substitution. Rate limited: 60 req/min.

Role Required
admin, operator Yes
curl -X POST http://localhost:9100/v1/templates \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "code-review",
    "description": "Standard code review session",
    "prompt": "Review {{repo}} for bugs and style issues",
    "workDir": "/home/user/repos/{{repo}}",
    "permissionMode": "bypassPermissions"
  }'

Request body:

Parameter Type Required Description
name string yes Template name (max 100 chars)
description string no Description (max 500 chars)
sessionId string (UUID) no Copy settings from an existing session
workDir string conditional Working directory (required if no sessionId)
prompt string no Template prompt with {{variable}} substitution (max 100k chars)
claudeCommand string no Claude Code CLI arguments (max 500 chars)
env object no Environment variables map
stallThresholdMs number no Stall timeout (1–3600000ms)
permissionMode string no default, bypassPermissions, plan, acceptEdits, dontAsk, auto
autoApprove boolean no Skip permission prompts
memoryKeys string[] no Pre-load memory entries (max 50)

Response (201 Created): Template object.


Get Template

GET /v1/templates/:id

Returns a single template by ID.

Role Required
admin, operator, viewer Yes
curl http://localhost:9100/v1/templates/tpl-abc123 \
  -H "Authorization: Bearer $TOKEN"

Response: Template object.

Errors:

Status Condition
404 Template not found

Update Template

PUT /v1/templates/:id

Partially updates a template. Only include fields to change.

Role Required
admin, operator Yes
curl -X PUT http://localhost:9100/v1/templates/tpl-abc123 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"description": "Updated description", "prompt": "New prompt {{var}}"}'

Request body: Partial template object (any fields from create schema).

Response: Updated template object.

Errors:

Status Condition
404 Template not found

Delete Template

DELETE /v1/templates/:id

Deletes a template by ID.

Role Required
admin, operator Yes
curl -X DELETE http://localhost:9100/v1/templates/tpl-abc123 \
  -H "Authorization: Bearer $TOKEN"

Response: { "ok": true }

Errors:

Status Condition
404 Template not found

11. Memory Bridge

Key-value memory store for cross-session context sharing.

Prerequisite: Memory endpoints require memoryBridge.enabled: true in your Aegis config. Without this setting, the routes are not registered and will return 404.

memoryBridge:
  enabled: true
  persistPath: ~/.aegis/memory.json   # optional
  reaperIntervalMs: 60000             # optional

Set Memory Entry

POST /v1/memory

Writes a memory entry with optional TTL.

curl -X POST http://localhost:9100/v1/memory \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"key": "project/context", "value": "Using Fastify v5", "ttlSeconds": 3600}'

Request body:

Parameter Type Required Description
key string yes Memory key (max 256 chars)
value string yes Memory value (max 100 KB)
ttlSeconds number no TTL in seconds (max 30 days)

Response: { "ok": true, "entry": { ... } }

Errors:

Status Condition
400 Invalid key format
413 Value exceeds maximum size

Get Memory Entry

GET /v1/memory/:key

Reads a memory entry by key.

curl http://localhost:9100/v1/memory/project%2Fcontext \
  -H "Authorization: Bearer $TOKEN"

Note: Keys containing / must be URL-encoded (%2F) in GET and DELETE paths. Alternatively, use GET /v1/memory?prefix=project/ to list and find entries.

Response: { "entry": { "key": "project/context", "value": "Using Fastify v5", "ttlSeconds": 3600 } }

Errors:

Status Condition
404 Key not found

List Memory Entries

GET /v1/memory

Lists all entries, optionally filtered by prefix.

curl "http://localhost:9100/v1/memory?prefix=project/" \
  -H "Authorization: Bearer $TOKEN"

Query parameters:

Parameter Type Description
prefix string Filter entries by key prefix

Response: { "entries": [...] }


Delete Memory Entry

DELETE /v1/memory/:key

Deletes a memory entry by key.

curl -X DELETE http://localhost:9100/v1/memory/project%2Fcontext \
  -H "Authorization: Bearer $TOKEN"

Response: { "ok": true }

Errors:

Status Condition
404 Key not found

Scoped Memory Retrieval

GET /v1/memories

Lists memory entries filtered by scope prefix.

curl "http://localhost:9100/v1/memories?scope=project" \
  -H "Authorization: Bearer $TOKEN"

Query parameters:

Parameter Type Required Description
scope string yes One of: project, user, team

Response: { "scope": "project", "entries": [...] }

Errors:

Status Condition
400 Invalid scope

12. Webhooks

Claude Code hook receiver and webhook delivery inspection.

Generic Hook Callback

POST /v1/hooks/:eventName

Inbound callback endpoint for Claude Code hooks. Requires hook secret if configured.

curl -X POST "http://localhost:9100/v1/hooks/Stop?sessionId=<session-uuid>" \
  -H "X-Hook-Secret: <hook-secret>" \
  -H "Content-Type: application/json" \
  -d '{}'

Authentication:

  • Send session ID via X-Session-Id header (or sessionId query fallback).
  • Send hook secret via X-Hook-Secret header.
  • Query-param secret is deprecated (logs warning unless AEGIS_HOOK_SECRET_HEADER_ONLY=true).

Path parameters:

Parameter Type Description
eventName string Hook event name (e.g. Stop, PreToolUse, PermissionRequest)

Request body: Validated against hookBodySchema. Varies by event type.

Response: 200 OK with event-specific response body. Decision events (PreToolUse, PermissionRequest) return approval/rejection.

Errors:

Status Condition
400 Unknown event name, missing session ID, invalid body
400 Invalid session ID format (must be UUID)
401 No valid session ID provided (must use X-Session-Id header or ?sessionId= query param)
401 Invalid hook secret
401 Hook secret sent via query param in header-only mode
404 Session not found

Dead-Letter Queue

GET /v1/webhooks/dead-letter

Lists failed webhook deliveries across all channels for inspection and retry.

Role Required
admin Yes
curl http://localhost:9100/v1/webhooks/dead-letter \
  -H "Authorization: Bearer $TOKEN"

Response: Array of failed delivery records.


Webhook Delivery History

GET /v1/hooks/:id/deliveries

Returns delivery history for a specific webhook.

curl http://localhost:9100/v1/hooks/hook-abc123/deliveries \
  -H "Authorization: Bearer $TOKEN"

Response: Array of delivery records with timestamps and status codes.


13. Events (SSE Stream)

⚠️ Auth required: SSE endpoints do not accept regular Bearer tokens. You must first obtain an SSE token via POST /v1/auth/sse-token, then pass it either as a query parameter (?token=<sse-token>) or as a Bearer header (Authorization: Bearer sse_...). Using a regular API key returns 401 Unauthorized — SSE token required for event streams.

Global SSE Event Stream

GET /v1/events

Server-Sent Events stream aggregating events from all active sessions.

# Step 1: Get an SSE token
curl -s -X POST http://localhost:9100/v1/auth/sse-token \
  -H "Authorization: Bearer $API_KEY"
# Step 2: Connect to the stream
curl -N "http://localhost:9100/v1/events?token=$SSE_TOKEN"

Authentication: SSE token via query parameter (?token=<sse-token>) or Bearer header (Authorization: Bearer sse_...). Regular API keys are rejected.

Event types: connected, heartbeat, session.created, session.idle, session.working, session.stalled, session.killed, permission.requested, permission.granted, permission.denied, message.user, status.*, verification.*, subagent_start, subagent_stop, circuit_breaker.

Rate limited: Per-IP and global connection limits apply.

Event replay: Supports Last-Event-ID header for replay of missed events.

Tenant scoping: Events are filtered by the caller's tenant. Admin/master keys see all events.

Connection events:

data: {"event":"connected","timestamp":"2026-04-22T10:00:00.000Z","data":{"activeSessions":5}}

Usage & Metering

Billing and metering data with time-range filtering.

Get Total Usage Summary

GET /v1/usage

Returns total usage across all sessions with optional time-range and key filters.

Role Required
admin, operator Yes
curl "http://localhost:9100/v1/usage?from=2026-04-01T00:00:00Z&to=2026-04-28T23:59:59Z" \
  -H "Authorization: Bearer $TOKEN"

Query parameters:

Parameter Type Description
from ISO 8601 Start of time window (inclusive)
to ISO 8601 End of time window (inclusive)
keyId string Filter to a specific API key

Response:

{
  "schema_version": 1,
  "totalSessions": 150,
  "totalTokens": 4200000,
  "totalSpendUsd": 84.50,
  "rate_tiers": [
    { "tier": "free", "tokensIncluded": 100000, "pricePerMillionTokens": 0 },
    { "tier": "standard", "tokensIncluded": 1000000, "pricePerMillionTokens": 20 }
  ]
}

Get Per-Key Usage Breakdown

GET /v1/usage/by-key

Returns usage broken down by API key. Admin only.

Role Required
admin Yes
curl "http://localhost:9100/v1/usage/by-key?from=2026-04-01T00:00:00Z" \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "schema_version": 1,
  "keys": [
    { "keyId": "ak_abc123", "keyName": "ci-bot", "sessions": 80, "tokens": 2200000, "spendUsd": 44.00 }
  ],
  "total_keys": 1
}

Get Per-Session Usage

GET /v1/usage/sessions/:id

Returns usage records for a specific session.

Role Required
admin, operator Yes
curl "http://localhost:9100/v1/usage/sessions/sess-xyz?from=2026-04-01T00:00:00Z" \
  -H "Authorization: Bearer $TOKEN"

Query parameters:

Parameter Type Description
from ISO 8601 Start of time window
to ISO 8601 End of time window

Response:

{
  "schema_version": 1,
  "sessionId": "sess-xyz",
  "records": [
    { "timestamp": "2026-04-22T10:30:00.000Z", "inputTokens": 1500, "outputTokens": 800, "costUsd": 0.05 }
  ],
  "total_records": 1
}

Get Rate Tier Configuration

GET /v1/usage/tiers

Returns the current rate tier configuration. No auth required.

curl http://localhost:9100/v1/usage/tiers

Response:

{
  "schema_version": 1,
  "tiers": [
    { "tier": "free", "tokensIncluded": 100000, "pricePerMillionTokens": 0 },
    { "tier": "standard", "tokensIncluded": 1000000, "pricePerMillionTokens": 20 }
  ]
}

Dashboard Authentication

Browser-based session management for the Aegis dashboard.

OIDC Login

GET /auth/login

Initiates OIDC login flow. Redirects to the IdP authorization endpoint.


OIDC Callback

GET /auth/callback

Handles the OIDC callback. Sets a dashboard session cookie and redirects to /dashboard/.


Session Info

GET /auth/session

Returns current dashboard session info (OIDC or token-based).

curl http://localhost:9100/auth/session \
  -H "Cookie: aegis-dashboard-session=..."

Response:

{
  "oidcAvailable": true,
  "authMethod": "oidc",
  "authenticated": true,
  "userId": "...",
  "role": "admin"
}

Logout

POST /auth/logout

Terminates the dashboard session. Clears the session cookie. May redirect to IdP end-session URL.

curl -X POST http://localhost:9100/auth/logout \
  -H "Cookie: aegis-dashboard-session=..."

Response: 204 No Content or 303 See Other (IdP logout redirect).


WebSocket Terminal Streaming

Aegis streams live terminal output to connected dashboard clients over WebSocket.

Endpoint: WS /v1/sessions/:id/terminal

Connection

const ws = new WebSocket('ws://localhost:9100/v1/sessions/abc123/terminal', {
  headers: { Authorization: 'Bearer your-token' }
});

Authentication

  • Browser clients: Send { type: "auth", token: "..." } as the first message (within 5 seconds).
  • Non-browser clients: Authorization: Bearer <token> header.
  • Per-connection rate limiting: 10 messages/second.

Message Protocol

Server → Client

Type Fields Description
pane content Full pane catchup (sent on connect)
stream data Incremental PTY output chunk
status status Current UI state (idle, working, etc.)
error message Error notification

Client → Server

Type Fields Description
auth token Authentication handshake (first message)
input text Send keystrokes to the session
resize cols, rows Resize the terminal

Unversioned Aliases

All core session endpoints are available without the /v1 prefix for backward compatibility:

v1 Endpoint Alias
POST /v1/sessions POST /sessions
GET /v1/sessions/:id/read GET /sessions/:id/read
POST /v1/sessions/:id/send POST /sessions/:id/send
POST /v1/sessions/:id/interrupt POST /sessions/:id/interrupt
DELETE /v1/sessions/:id DELETE /sessions/:id
POST /v1/sessions/:id/spawn POST /sessions/:id/spawn
POST /v1/sessions/:id/fork POST /sessions/:id/fork
PUT /v1/sessions/:id/permissions PUT /sessions/:id/permissions
PUT /v1/sessions/:id/permission-profile PUT /sessions/:id/permission-profile

Convenience Aliases: /v1/keys

Alias Canonical Method
/v1/keys /v1/auth/keys GET (list), POST (create)
/v1/keys/:id /v1/auth/keys/:id DELETE (revoke)

Error Responses

All endpoints return errors in a consistent envelope format:

{
  "error": "Session not found",
  "code": "SESSION_NOT_FOUND",
  "statusCode": 404
}

HTTP Status Codes

Status Code Meaning
200 Success
201 Created
204 No Content (successful deletion/logout)
400 Bad request (invalid parameters)
401 Unauthorized (missing or invalid token)
403 Forbidden (valid token but insufficient permissions)
404 Session or resource not found
409 Conflict (already exists, wrong state)
422 Unprocessable entity (validation failed)
429 Rate limited or quota exceeded
500 Internal server error
501 Not implemented (feature not available in current mode)
502 Bad gateway (upstream/IdP failure)
503 Service unavailable (feature not configured)
504 Gateway timeout

Aegis Error Codes

Every error response includes an Aegis-specific code field for programmatic handling:

Error Code HTTP Status Meaning
SESSION_NOT_FOUND 404 Session deleted, wrong state, or never existed
SESSION_CREATE_FAILED 500 Claude Code launch failed
PERMISSION_REJECTED 409 Permission request answered with reject
ACM_TIMEOUT 504 Session operation timed out (retryable)
ACM_ERROR 500 Session operation failed
VALIDATION_ERROR 422 Request body or parameters failed Zod validation
AUTH_ERROR 401 Authentication failed
RATE_LIMITED 429 Per-key rate limit exceeded
QUOTA_EXCEEDED 429 Per-key session/token/spend quota exceeded
NETWORK_ERROR 502 Transient network or I/O failure (retryable)
INTERNAL_ERROR 500 Unexpected internal error
TENANT_WORKDIR_DENIED 403 workDir outside tenant root
CC_VERSION_TOO_OLD 422 Claude Code version below minimum
ALREADY_PAUSED 409 Session already paused
NO_ACTIVE_PAUSE 409 No active pause to start intervention
NO_ACTIVE_INTERVENTION 409 No active intervention to complete
NOT_PAUSED 409 Session not paused for resume

Session Hooks

Circuit Breaker (StopFailure Protection)

When a user-defined Stop hook returns ok: false, Claude Code retries in a loop — burning the session. Aegis detects rapid StopFailure events and trips a circuit breaker, returning ok: true to break the retry loop.

Behavior:

  • After HOOK_CIRCUIT_BREAKER_MAX failures within HOOK_CIRCUIT_BREAKER_WINDOW_MS, the breaker trips.
  • Aegis returns { ok: true } and emits a circuit_breaker SSE event.
  • The breaker stays tripped for the session's lifetime (no auto-reset).
  • A successful Stop hook event resets the breaker.
Variable Default Range Description
HOOK_CIRCUIT_BREAKER_MAX 5 1–100 Failures before breaker trips
HOOK_CIRCUIT_BREAKER_WINDOW_MS 60000 1000–3600000 Sliding window in milliseconds

Premature Termination Detection

Background agents can terminate mid-thought due to upstream turn limits. Aegis detects this by tracking tool use counts:

  • Detection rule: Session ends with toolUseCount > 0 AND toolUseCount <= threshold AND duration >= minimum.
  • Default thresholds: <= 30 tool uses, >= 30,000ms (30s) duration.
Variable Default Description
PREMATURE_TERMINATION_MIN_TOOLS 30 Max tool uses before flagging
PREMATURE_TERMINATION_MIN_DURATION_MS 30000 Minimum session duration (ms)

Hook Payload Size Warning

Claude Code silently truncates hook payloads exceeding ~2KB. Aegis logs a warning and emits a system SSE event when any hook payload exceeds 1.5KB. Keep hook payloads under 1.5KB to avoid truncation.


Multi-Tenancy

Aegis supports multi-tenant deployments via tenantId on API keys, sessions, and audit records.

  • API keys can be assigned a tenantId at creation.
  • Sessions inherit tenantId from the creating API key.
  • Non-admin keys are scoped to their tenant.
  • Admin keys bypass tenant scoping.

Tenant Workdir Namespacing

Restrict each tenant's sessions to a specific directory root:

tenantWorkdirs:
  tenant-a:
    root: /tenants/tenant-a
    allowedPaths: [projects, workspace]

Cross-tenant violations return 403 Forbidden with audit trail.

See ADR-0025 for the design decision.