Execution Receipts¶
Execution receipts are HermesX's governance proof for tool side effects. An audit log proves that an API request happened. A receipt proves that a specific tool execution was attempted, with the tenant, session, user, tool name, bounded input/output, status, duration, idempotency key, and trace correlation recorded.
中文速览:执行回执用于证明 Agent 工具副作用。审计日志回答"谁调用了哪个 API", 执行回执回答"哪个工具在什么上下文中执行,结果是什么,是否可安全重试"。
Why Receipts Exist¶
Agent conversations can branch, retry, call tools, and partially fail. Enterprise governance needs a durable record at the side-effect boundary, not only at the HTTP request boundary.
| Question | Audit log | Execution receipt |
|---|---|---|
| Who called the API? | Yes | Inherited through tenant/user fields |
| Which tool ran? | No | Yes |
| What input/output crossed the tool boundary? | No | Yes, truncated to the runtime limit |
| Did the side effect complete, fail, or time out? | Request-level only | Tool-level status |
| Can a retry duplicate the side effect? | No | Controlled by idempotency_id |
| Can traces connect API, tool, and telemetry? | Request ID | trace_id plus session/request context |
Receipts are not a full event-sourcing log. They are immutable evidence for a tool call at the point where the agent runtime leaves pure language generation.
Receipt Object¶
| Field | Type | Governance meaning |
|---|---|---|
id |
string | Unique receipt identifier returned to auditors and operators |
tenant_id |
string | Tenant isolation boundary |
session_id |
string | Agent or workflow session that caused the tool call |
user_id |
string | User or workflow actor that triggered execution |
tool_name |
string | Tool that crossed the side-effect boundary |
input |
string | Serialized tool input, truncated by runtime policy |
output |
string | Serialized result or error, truncated by runtime policy |
status |
string | success, error, or timeout |
duration_ms |
integer | Tool wall-clock duration |
idempotency_id |
string | Optional duplicate-call key scoped to the tenant |
trace_id |
string | Optional distributed trace correlation ID |
created_at |
timestamp | Persistence time |
Current PostgreSQL storage enforces tenant isolation with RLS and keeps a unique
index on (tenant_id, idempotency_id) when the idempotency value is non-empty.
Creation and Query Contract¶
Receipts are created by the runtime/store path when HermesX records a tool execution. In this worktree, the public tenant HTTP API exposes read/query endpoints:
GET /v1/execution-receiptsGET /v1/execution-receipts/{id}
Do not design external integrations around POST /v1/execution-receipts unless
that route is present in the deployed server you are targeting. The safer
contract is: trigger a governed agent or workflow action, then query receipts by
session, tool, status, or receipt ID.
API Examples¶
1. Start an Agent Task¶
Use a stable X-Request-ID and X-Hermes-Session-Id so the API audit log,
conversation, and receipt query can be correlated.
REQUEST_ID="demo-agent-first-001"
SESSION_ID="sess_agent_first_minimal_demo"
curl -sS -D /tmp/hermesx-agent.headers \
-X POST "http://localhost:8080/v1/agent/chat" \
-H "Authorization: Bearer $HERMES_CHAT_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Request-ID: $REQUEST_ID" \
-H "X-Hermes-Session-Id: $SESSION_ID" \
-d '{
"model": "mock",
"messages": [
{
"role": "user",
"content": "Use one safe read-only tool if available, then answer in one sentence."
}
],
"include_agentic_blocks": true
}'
The response headers include X-Request-ID and, when tracing is active,
X-Trace-ID.
2. List Receipts for the Session¶
curl -sS "http://localhost:8080/v1/execution-receipts?session_id=$SESSION_ID&limit=20" \
-H "Authorization: Bearer $HERMES_AUDIT_API_KEY"
Example response:
{
"execution_receipts": [
{
"id": "rcpt_demo_001",
"tenant_id": "tenant_demo",
"session_id": "sess_agent_first_minimal_demo",
"user_id": "user_demo",
"tool_name": "read_file",
"input": "{\"path\":\"docs/SECURITY_MODEL.md\"}",
"output": "{\"bytes\":512,\"redacted\":false}",
"status": "success",
"duration_ms": 17,
"idempotency_id": "demo-agent-first-001/read_file/1",
"trace_id": "0af7651916cd43dd8448eb211c80319c",
"created_at": "2026-05-24T04:00:00Z"
}
],
"total": 1
}
3. Fetch the Receipt by ID¶
RECEIPT_ID="rcpt_demo_001"
curl -sS "http://localhost:8080/v1/execution-receipts/$RECEIPT_ID" \
-H "Authorization: Bearer $HERMES_AUDIT_API_KEY"
4. Correlate the API Audit Log¶
curl -sS "http://localhost:8080/v1/audit-logs?limit=50" \
-H "Authorization: Bearer $HERMES_AUDIT_API_KEY"
Find the audit entry whose request_id matches the X-Request-ID used for the
agent call. Then use session_id and trace_id from receipts to connect the API
request, agent session, tool execution, and distributed trace.
Idempotency and Duplicate Calls¶
idempotency_id is the receipt-level duplicate-call key. It is scoped by tenant,
so two tenants may reuse the same value without conflict.
Runtime semantics:
- If
idempotency_idis absent, every tool execution may create a separate receipt. - If
idempotency_idis present and no prior receipt exists for the same tenant, the runtime executes the tool and stores the receipt. - If the same
(tenant_id, idempotency_id)appears again, the runtime should return the prior receipt/output and skip re-execution. - If a retry carries different input for an existing idempotency key, the first receipt remains authoritative. Treat the key as naming one logical side effect, not a mutable request body.
Implication: clients should generate the idempotency key before attempting a tool side effect and reuse it only for retries of that same logical operation.
Workflow Relationship¶
Free-form /v1/agent/chat can produce receipts when the agent calls tools, but
the conversation itself is not a fixed SOP. A workflow agent_task is different:
the published workflow version owns the order, retry surface, human approvals,
and step state, while the agent runtime owns the language/tool loop inside that
single governed node.
See Workflow and Agent Runtime Boundary for the full boundary contract.