API Reference¶
Complete endpoint documentation for the HermesX Enterprise Agent Platform. All authenticated endpoints require an
Authorization: Bearer <token>header.
Base Information¶
| Field | Value |
|---|---|
| Base URL | http://localhost:8080 |
| Authentication | Bearer Token (Static Token / API Key / JWT) |
| Content Type | application/json |
| Rate Limiting | Returned via X-RateLimit-Limit and X-RateLimit-Remaining response headers |
Public Endpoints (No Authentication Required)¶
GET /health/live¶
Liveness probe. Returns 200 as soon as the service starts.
curl http://localhost:8080/health/live
# {"status":"ok"}
GET /health/ready¶
Readiness probe. Checks database connection status.
curl http://localhost:8080/health/ready
# {"status":"ready","database":"ok"}
GET /metrics¶
Prometheus metrics endpoint. Returns text/plain format.
curl http://localhost:8080/metrics
Metrics include:
- hermes_http_requests_total{method, path, status, tenant_id} — Total HTTP requests
- hermes_http_request_duration_seconds{method, path, tenant_id} — Request latency histogram
- hermes_http_requests_in_flight — Current concurrent request count
Admin Endpoints (Require admin Role)¶
The following endpoints require admin role. Access using the Static Token (HERMES_ACP_TOKEN) or an API Key with admin role.
Bootstrap /admin/v1/bootstrap¶
GET /admin/v1/bootstrap/status — Check if initialization is required¶
Public endpoint, no authentication required.
curl http://localhost:8080/admin/v1/bootstrap/status
# {"bootstrap_required":true}
POST /admin/v1/bootstrap — Create the first default tenant admin key¶
Only available when no default tenant admin key exists. This endpoint does not go through the admin scope middleware, but must carry HERMES_ACP_TOKEN and enforces independent rate limiting per source IP (default HERMES_BOOTSTRAP_RATE_LIMIT_RPM=5).
curl -X POST http://localhost:8080/admin/v1/bootstrap \
-H "Authorization: Bearer $HERMES_ACP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"initial-admin-key"}'
The key in the response is returned only once.
Tenant Management /v1/tenants¶
POST /v1/tenants — Create a tenant¶
curl -X POST http://localhost:8080/v1/tenants \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"plan": "pro",
"rate_limit_rpm": 120,
"max_sessions": 50
}'
Response:
{
"id": "a1b2c3d4-...",
"name": "Acme Corp",
"plan": "pro",
"rate_limit_rpm": 120,
"max_sessions": 50,
"created_at": "2026-04-29T12:00:00Z",
"updated_at": "2026-04-29T12:00:00Z"
}
When creating a tenant with MinIO configured, the system asynchronously provisions all 81 built-in skills and a default SOUL.md personality file for the new tenant.
GET /v1/tenants — List all tenants¶
curl http://localhost:8080/v1/tenants \
-H "Authorization: Bearer $ADMIN_TOKEN"
Response:
{
"tenants": [
{
"id": "a1b2c3d4-...",
"name": "Acme Corp",
"plan": "pro",
"rate_limit_rpm": 120,
"max_sessions": 50,
"created_at": "...",
"updated_at": "..."
}
]
}
GET /v1/tenants/{id} — Get a single tenant¶
curl http://localhost:8080/v1/tenants/a1b2c3d4-... \
-H "Authorization: Bearer $ADMIN_TOKEN"
PUT /v1/tenants/{id} — Update a tenant¶
curl -X PUT http://localhost:8080/v1/tenants/a1b2c3d4-... \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"plan": "enterprise", "rate_limit_rpm": 300}'
DELETE /v1/tenants/{id} — Delete a tenant¶
curl -X DELETE http://localhost:8080/v1/tenants/a1b2c3d4-... \
-H "Authorization: Bearer $ADMIN_TOKEN"
API Key Management /v1/api-keys¶
POST /v1/api-keys — Create an API Key¶
curl -X POST http://localhost:8080/v1/api-keys \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "production-key",
"tenant_id": "a1b2c3d4-...",
"roles": ["user"]
}'
Response:
{
"id": "key-uuid-...",
"key": "hk_a1b2c3d4e5f6...",
"prefix": "hk_a1b2c",
"name": "production-key",
"tenant_id": "a1b2c3d4-...",
"roles": ["user"],
"created_at": "..."
}
The
keyfield is returned only once at creation time. API Keys are stored as SHA-256 hashes in the database and cannot be retrieved again.
GET /v1/api-keys — List all API Keys¶
curl http://localhost:8080/v1/api-keys \
-H "Authorization: Bearer $ADMIN_TOKEN"
DELETE /v1/api-keys/{id} — Revoke an API Key¶
curl -X DELETE http://localhost:8080/v1/api-keys/key-uuid-... \
-H "Authorization: Bearer $ADMIN_TOKEN"
Audit Logs /v1/audit-logs¶
GET /v1/audit-logs — Query audit logs¶
Requires auditor role.
curl "http://localhost:8080/v1/audit-logs?limit=50" \
-H "Authorization: Bearer hk_your_api_key"
Supported query parameters: action, from (ISO 8601 time), to, limit (default 50), offset.
Each audit record contains: tenant_id, user_id, action, detail, request_id, status_code, latency_ms, created_at.
Execution Receipts /v1/execution-receipts¶
An execution receipt is an immutable audit record created each time HermesX runs a tool on behalf of a user. Receipts capture the full input/output payload, execution duration, final status, and an optional caller-supplied idempotency key. They are isolated by tenant and are never modified after creation.
Required role: auditor
Receipt object¶
| Field | Type | Description |
|---|---|---|
id |
string (UUID) | Unique receipt identifier |
tenant_id |
string (UUID) | Tenant that owns this receipt |
session_id |
string | Session in which the tool was called |
user_id |
string | User who triggered the execution |
tool_name |
string | Name of the tool that was executed |
input |
string | Serialised input passed to the tool |
output |
string | Serialised output returned by the tool |
status |
string | "success", "error", or "timeout" |
duration_ms |
integer | Wall-clock execution time in milliseconds |
idempotency_id |
string | Caller-supplied deduplication key (optional) |
trace_id |
string | Distributed trace ID for cross-service correlation (optional) |
created_at |
string (RFC 3339) | Timestamp when the receipt was persisted |
Status values¶
| Value | Meaning |
|---|---|
"success" |
Tool completed and returned a usable result |
"error" |
Tool returned an error; check output for the error detail |
"timeout" |
Execution was cancelled because it exceeded the configured deadline |
Idempotency¶
When your agent or orchestration layer submits a tool result to HermesX, you can include an idempotency_id — any opaque string that uniquely identifies this logical execution in your system (for example, a job ID or a UUID you generate before the call).
If a receipt with the same (tenant_id, idempotency_id) pair already exists, HermesX returns the existing receipt without creating a duplicate. This means it is safe to retry a receipt submission after a network failure or timeout: you will get the original receipt back, and your audit log will not contain duplicates.
Important constraints:
- idempotency_id values are scoped to your tenant; the same value in a different tenant creates a separate receipt.
- Once a receipt is stored it cannot be updated. An idempotency match always returns the original record, even if the retry carries different input/output values.
- Omitting idempotency_id creates a new receipt on every call.
Relationship to Audit Logs¶
Execution receipts and audit logs serve complementary purposes:
| Execution Receipt | Audit Log | |
|---|---|---|
| What it records | A single tool call: input, output, duration | An HTTP request: action, status code, latency |
| Granularity | Tool-level | API request–level |
| Payload | Full tool I/O | Action type + metadata |
| Use for | Debugging tool behaviour, billing, replay | Access history, compliance, security review |
A single agent turn that calls three tools produces three execution receipts and one audit log entry.
GET /v1/execution-receipts — List tool execution receipts¶
curl "http://localhost:8080/v1/execution-receipts?session_id=sess-abc&limit=20" \
-H "Authorization: Bearer hk_your_api_key"
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
session_id |
string | — | Filter to a single session |
tool_name |
string | — | Filter by tool name (exact match) |
status |
string | — | Filter by status: success, error, or timeout |
limit |
integer | 50 | Items per page (max 500) |
offset |
integer | 0 | Pagination offset |
Response:
{
"execution_receipts": [
{
"id": "3f7a1c2d-...",
"tenant_id": "8e4b0c1a-...",
"session_id": "sess-abc",
"user_id": "usr-xyz",
"tool_name": "code-review",
"input": "{\"language\":\"go\",\"snippet\":\"...\"}",
"output": "{\"issues\":[],\"score\":9}",
"status": "success",
"duration_ms": 842,
"idempotency_id": "job-2026-04-29-001",
"trace_id": "abc123",
"created_at": "2026-04-29T12:00:00Z"
}
],
"total": 1
}
GET /v1/execution-receipts/{id} — Get a single receipt by ID¶
curl "http://localhost:8080/v1/execution-receipts/3f7a1c2d-..." \
-H "Authorization: Bearer hk_your_api_key"
Returns the receipt object directly (not wrapped in an array). Returns 404 if the receipt does not exist or belongs to a different tenant.
Looking up a receipt by idempotency ID¶
The /v1/execution-receipts list endpoint does not currently support filtering by idempotency_id directly. To check whether a specific idempotency key has already been recorded, list receipts for the relevant session and filter client-side, or use the internal GetByIdempotencyID store method if building server-side integrations.
Execution receipts are written by the internal tool execution path. The public API exposes query-only access.
GDPR Compliance /v1/gdpr¶
GET /v1/gdpr/export — Export user data¶
curl http://localhost:8080/v1/gdpr/export \
-H "Authorization: Bearer $ADMIN_TOKEN"
DELETE /v1/gdpr/data — Delete user data¶
curl -X DELETE http://localhost:8080/v1/gdpr/data \
-H "Authorization: Bearer hk_your_api_key"
POST /v1/gdpr/cleanup-minio — Clean up orphaned MinIO objects¶
Cleans media files that exist in MinIO for the current tenant but have no database references.
curl -X POST http://localhost:8080/v1/gdpr/cleanup-minio \
-H "Authorization: Bearer hk_your_api_key"
User Endpoints (Authentication Required, user or admin Role)¶
POST /v1/chat/completions — Send a Chat Request¶
OpenAI-compatible chat interface. Automatically associated with the tenant of the requester.
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer hk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "mock",
"messages": [
{"role": "user", "content": "Hello!"}
]
}'
Response:
{
"id": "chatcmpl-...",
"object": "chat.completion",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "..."
},
"finish_reason": "stop"
}
]
}
Supported request headers:
| Header | Description |
|---|---|
X-Hermes-Session-Id |
Optional. Specify a session ID to maintain multi-turn conversations; when omitted, the server creates a new session and returns the actual ID in the response header |
X-Hermes-User-Id |
Specify a user ID for memory and profile isolation (defaults to API Key identity if not provided) |
Chat requests automatically inject the following context (requires MinIO and PostgreSQL to be configured):
- Soul: Loads the tenant's
SOUL.mdpersonality file from MinIO - Memory and profiles: Loads user-level memories and profiles from PostgreSQL
- Skill summary: Loads a list of the tenant's installed skills from MinIO
POST /v1/agent/chat — Agent Chat Interface (Alias)¶
Same functionality as /v1/chat/completions, providing the Agent tool call loop. Responses include X-Hermes-Session-Id; clients should save that value and send it back as the same request header on later turns.
curl -X POST http://localhost:8080/v1/agent/chat \
-H "Authorization: Bearer hk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "mock",
"messages": [{"role": "user", "content": "Hello!"}],
"include_agentic_blocks": true
}'
include_agentic_blocks defaults to false. When set to true:
- JSON responses include
agentic_blocksfor debugging sanitized Eino AgenticMessage content blocks. - SSE streaming responses emit additional
event: agentic_blockevents whosedatapayload is one block JSON object.
Failure semantics:
- Agent runtime failures return
502and do not persist user/assistant messages or update token counters. - Streaming runtime failures emit
event: error, thendata: [DONE], without fabricatingfinish_reason: "stop". - Concurrent requests for the same
tenant/sessionare serialized by the handler to avoid duplicate message, checkpoint, and token writes.
GET /v1/me — Current Identity Information¶
Returns the identity, tenant, and roles of the currently authenticated user.
curl http://localhost:8080/v1/me \
-H "Authorization: Bearer hk_your_api_key"
Response:
{
"identity": "key-uuid-...",
"tenant_id": "a1b2c3d4-...",
"roles": ["user"],
"auth_method": "api_key"
}
GET /v1/usage — Usage Statistics¶
Returns usage statistics for the current tenant. When UsageStore is configured, this endpoint returns day/month aggregates from usage_records; otherwise it keeps the legacy session token summary.
curl http://localhost:8080/v1/usage \
-H "Authorization: Bearer hk_your_api_key"
Common query parameters: from, to, granularity=day|month.
GET /v1/usage/details — Session Usage Details¶
Available when UsageStore is configured. Returns usage records for one tenant-scoped session.
curl "http://localhost:8080/v1/usage/details?session_id=sess-..." \
-H "Authorization: Bearer hk_your_api_key"
Session Management /v1/sessions¶
GET /v1/sessions — List user sessions¶
Returns a paginated list of sessions for the current tenant (optionally filtered by user_id).
curl "http://localhost:8080/v1/sessions?limit=50&offset=0&user_id=xxx" \
-H "Authorization: Bearer hk_your_api_key"
Supported query parameters: limit (default 50), offset (default 0), user_id.
Response:
{
"sessions": [
{
"id": "sess-...",
"tenant_id": "uuid-...",
"user_id": "user-...",
"model": "mock",
"created_at": "...",
"updated_at": "..."
}
],
"total": 10
}
GET /v1/sessions/{id} — Get session details¶
Returns the specified session and its message history.
curl http://localhost:8080/v1/sessions/sess-... \
-H "Authorization: Bearer hk_your_api_key"
DELETE /v1/sessions/{id} — Delete a session¶
curl -X DELETE http://localhost:8080/v1/sessions/sess-... \
-H "Authorization: Bearer hk_your_api_key"
Long-Term Memory /v1/memories¶
GET /v1/memories — List user memories¶
Returns long-term memory entries for the current user (specified via the X-Hermes-User-Id header).
curl http://localhost:8080/v1/memories \
-H "Authorization: Bearer hk_your_api_key" \
-H "X-Hermes-User-Id: user-xxx"
Response:
{
"memories": [
{
"key": "preference",
"value": "prefers dark mode",
"created_at": "..."
}
],
"total": 5
}
DELETE /v1/memories/{key} — Delete a memory entry¶
curl -X DELETE http://localhost:8080/v1/memories/preference \
-H "Authorization: Bearer hk_your_api_key" \
-H "X-Hermes-User-Id: user-xxx"
Returns 204 No Content on success.
Skills Management /v1/skills¶
GET /v1/skills — List tenant skills¶
Returns all Skills installed for the current tenant, including source and modification status.
curl http://localhost:8080/v1/skills \
-H "Authorization: Bearer hk_your_api_key"
Response:
{
"tenant_id": "a1b2c3d4-...",
"skills": [
{
"name": "code-review",
"description": "Code review assistant",
"version": "1.0.0",
"source": "builtin",
"user_modified": false
},
{
"name": "my-custom-skill",
"description": "Custom business skill",
"version": "1.0.0",
"source": "user",
"user_modified": true
}
],
"total": 2
}
GET /v1/skills/{name} — Get skill content¶
Returns the full SKILL.md content of the specified skill (raw text).
curl http://localhost:8080/v1/skills/my-custom-skill \
-H "Authorization: Bearer hk_your_api_key"
PUT /v1/skills/{name} — Upload/update a skill¶
Uploads SKILL.md content as a tenant custom skill. After upload, the skill is marked as user_modified and will not be overwritten by system auto-sync.
curl -X PUT http://localhost:8080/v1/skills/my-custom-skill \
-H "Authorization: Bearer hk_your_api_key" \
-H "Content-Type: text/plain" \
-d '---
name: "my-custom-skill"
description: "Custom business skill"
version: "1.0.0"
---
# My Custom Skill
You are a specialized assistant for my business domain.'
Response:
{
"status": "uploaded",
"skill": "my-custom-skill"
}
Limit: maximum request body size is 1MB.
DELETE /v1/skills/{name} — Delete a skill¶
curl -X DELETE http://localhost:8080/v1/skills/my-custom-skill \
-H "Authorization: Bearer hk_your_api_key"
Returns 204 No Content on success.
GET /v1/openapi — OpenAPI Specification¶
Returns the OpenAPI 3.0 specification document in JSON format.
curl http://localhost:8080/v1/openapi
Error Codes¶
| HTTP Status | Description |
|---|---|
| 200 | Success |
| 204 | Success (no content, e.g., OPTIONS preflight) |
| 400 | Bad request parameters |
| 401 | Unauthenticated (missing or invalid token) |
| 403 | Insufficient permissions (role requirements not met) |
| 404 | Resource not found |
| 429 | Rate limit exceeded |
| 500 | Internal server error |
Rate Limiting¶
Each response includes rate limit information in the headers:
| Response Header | Description |
|---|---|
X-RateLimit-Limit |
Requests allowed in the current window (RPM) |
X-RateLimit-Remaining |
Remaining available requests |
Retry-After |
Returned when rate limited, suggested wait time in seconds (fixed 60s) |
Rate limiting is tracked per tenant — all API Keys under the same tenant share the quota. Unauthenticated requests are rate-limited by IP address.
CORS¶
Configured via the SAAS_ALLOWED_ORIGINS environment variable:
- Set to
*to allow all origins - Set to a comma-separated list of domains for precise control
Allowed methods: GET, POST, PUT, DELETE, OPTIONS
Allowed headers: Authorization, Content-Type, X-Hermes-Session-Id, X-Hermes-User-Id
Admin Sub-Routes /admin/*¶
Admin panel routes are authorized by governance domain: billing:*, audit:read, security:*, ops:*, tenant:*, key:*, and sharing:*. The legacy admin scope is retained only as explicit break-glass compatibility.
curl http://localhost:8080/admin/v1/pricing-rules \
-H "Authorization: Bearer $ADMIN_TOKEN"
Evolution Sharing Governance¶
| Method | Path | Scope | Description |
|---|---|---|---|
GET |
/admin/v1/evolution/sharing-policy |
sharing:read or security:read |
Get global sharing level |
GET |
/admin/v1/evolution/sharing-policy/history |
sharing:read or security:read |
List global sharing policy history |
PUT |
/admin/v1/evolution/sharing-policy |
sharing:write or security:write |
Set disabled / anonymous / trusted |
POST |
/admin/v1/evolution/sharing-policy/rollback |
sharing:write or security:write |
Roll back the global sharing policy to a prior version and mint a new version |
GET |
/admin/v1/evolution/tenants/{id}/sharing-policy |
sharing:read or tenant:read |
Get effective tenant sharing policy |
GET |
/admin/v1/evolution/tenants/{id}/sharing-policy/history |
sharing:read or tenant:read |
List tenant sharing policy history |
PUT |
/admin/v1/evolution/tenants/{id}/sharing-policy |
sharing:write or tenant:write |
Set tenant consumption/contribution policy |
POST |
/admin/v1/evolution/tenants/{id}/sharing-policy/rollback |
sharing:write or tenant:write |
Roll back the tenant sharing policy to a prior version and mint a new version |
POST |
/admin/v1/evolution/shared-knowledge/revoke |
sharing:write or security:write |
Revoke shared knowledge by tenant, task class, source, or time window |
Static Pages¶
When SAAS_STATIC_DIR is configured, the following routes are served from static files:
| Path | Description |
|---|---|
/ |
Homepage (index.html) |
/admin.html |
Admin panel |
/static/* |
Static assets directory |