Docs navigation

Audit events

Every agent action becomes an event in an append-only audit trail. Each event's hash commits to its content AND the previous event's hash, forming a per-agent chain — edit, delete, or reorder anything and verification fails.

Logging events

Node.js
// Purpose-built helpers set eventType/eventCategory for you:
await ProofLedger.logDecision({ action: "Chose refund path", workflowId });
await ProofLedger.logToolCall({ toolName: "refund_lookup", workflowId });
await ProofLedger.logApiCall({ apiEndpoint: "/v1/refunds", workflowId });
await ProofLedger.logWorkflowStep({ workflowId, action: "Done", status: "completed" });

// Or the general form:
await ProofLedger.logTrustEvent({
  eventType: "failed_authorization",   // task_received | decision | tool_call |
  eventCategory: "security",           //   api_call | workflow_step | error | custom
  action: "Attempted push to protected branch",
  inputSummary: "branch=main",         // summaries are clamped to 2000 chars
  metadata: { pr: 2113 },              // free-form JSON, stored verbatim
});

Categories: decision, tool_call, api_call, workflow, policy, security, system. Every event stores the policy decision it received and the trust score before/after.

Warning

Send summaries, not payloads. The ingest body is capped at 64 KB and summaries at 2,000 characters — an audit trail should record what happened, not mirror your data. Keep PII out of summaries; put references (order ids, ticket ids) in metadata instead.

The hash chain

What each event hash commits to
hash = SHA-256(canonical({
  id, agentId, eventType, eventCategory, action, timestamp,
  toolName, mcpServer, apiEndpoint, policyDecision,
  inputSummary, outputSummary,
  previousHash          // ← the previous event's hash (or the genesis hash)
}))

Chains are per-agent and strictly ordered (a unique sequence index makes concurrent appends safe — the losing writer retries against the new head, so chains never fork). Nothing in any application flow updates or deletes an audit event.

Verifying the chain

curl -X POST https://www.proofledger.dev/api/audit/verify \
  -H "authorization: Bearer $PROOFLEDGER_API_KEY" \
  -H "content-type: application/json" \
  -d '{"agentId": "support-agent"}'    # omit agentId to verify every chain

# → { "verification": { "valid": true, "totalEvents": 26,
#      "agents": [{ "agentId": "support-agent", "valid": true,
#                   "eventCount": 26, "issues": [] }] } }

Verification recomputes every hash and link. Failure modes are precise: hash_mismatch (an event was edited), broken_link (deleted or reordered), missing_genesis (truncated from the start). The dashboard's Audit trail page runs the same check with one click.

GET /api/events?agentId=support-agent&eventCategory=tool_call&q=refund&limit=50
GET /api/events/:eventId
GET /api/agents/:agentId/events?policyDecision=block