Agent audit
Recalled is built for human and AI agent actions, side by side. In 2026, half the actions in a typical SaaS are taken by AI agents (Claude, GPT, custom agents wired with tool calls), and the same audit log records them with the same hash chain, the same signatures, the same dashboard. Two small conventions make it the system of record for what your agents did, when, and on whose behalf.
The pattern
The agent itself does not call Recalled. Your backend orchestrates the agent, runs the tool calls, and logs the resulting actions. From Recalled's point of view, an agent is just an actor with a different actor.type.
Two conventions:
actor.typeis set to"agent"(or"ai_agent", pick one and stick with it).metadata.triggered_by_usercarries the human user id who started the conversation that led to this action.
client.events.emit({
action: "file.deleted",
actor: {
type: "agent",
id: "claude-sonnet-4.6",
name: "Support Triage Agent",
},
organization: "acme_corp",
targets: [{ type: "file", id: "f_42", name: "old-report.pdf" }],
metadata: {
triggered_by_user: "user_123",
conversation_id: "conv_xyz",
tool_call_id: "call_abc",
reasoning: "user asked to clean up old uploads",
confidence: "high",
model: "claude-sonnet-4.6",
tokens_used: 1240,
result: "success",
},
});Three events per tool call
For full traceability, log three events around each agent tool call:
// 1. The agent decided to call a tool
client.events.emit({
action: "agent.tool_called",
actor: { type: "agent", id: "claude-sonnet-4.6", name: "Support Agent" },
targets: [{ type: "tool", id: "delete_file" }],
metadata: {
triggered_by_user: "user_123",
conversation_id: "conv_xyz",
tool_args: { file_id: "f_42" },
},
});
// 2. The action itself (same as a human action)
client.events.emit({
action: "file.deleted",
actor: { type: "agent", id: "claude-sonnet-4.6", name: "Support Agent" },
targets: [{ type: "file", id: "f_42" }],
metadata: { triggered_by_user: "user_123", on_behalf_of: "user_123" },
});
// 3. The tool returned to the agent
client.events.emit({
action: "agent.tool_returned",
actor: { type: "agent", id: "claude-sonnet-4.6", name: "Support Agent" },
targets: [{ type: "tool_call", id: "call_abc" }],
metadata: {
triggered_by_user: "user_123",
result: "success",
duration_ms: 142,
},
});If the cardinality is a problem at scale, drop the tool_called and tool_returned events and keep only the action itself with rich metadata. The action event alone is enough for accountability.
Filtering: humans vs agents
Once actor.type=agent is set consistently, you can filter the dashboard or the API to see only agent activity, only human activity, or everything for one user:
- All agent activity this month:
?actor_type=agent(filter by metadata if you need it; today the API filters byactor_id) - All actions a specific human triggered, including those carried out by agents on their behalf: filter by
metadata.triggered_by_user(planned, today match by hand usinglist_eventsand post-filter) - Audit trail for a specific agent:
?actor_id=claude-sonnet-4.6
Receipts: replayable evidence
When an agent says "I deleted that file for you", you want it to back the claim with proof. Recalled issues a receipt for any event:
curl https://api.recalled.dev/v1/events/$EVENT_ID/receipt \
-H "Authorization: Bearer rec_live_..."You get a JSON object the agent can paste back into its reply, with a view_url (public webpage) and a verification_url (no-auth JSON endpoint). The recipient can verify cryptographically that the event happened, in what order, and untampered, without an API key.
Inside Claude Desktop, Cursor or any MCP client connected to Recalled, the get_event_receipt tool returns the same object so the agent can cite it without going through HTTP itself.
What auditors actually want to see
If a customer or auditor questions an agent action three months later, here is what makes it bulletproof:
- The action itself is in the audit log with
actor.type=agent. - The receipt verifies green: hash chain intact, HMAC signature valid.
metadata.triggered_by_userties the action to the human who initiated it.metadata.reasoning,metadata.confidence,metadata.modelgive context.metadata.conversation_idlets you pull the whole conversation if needed.
Public verification page
Every receipt has a public URL like https://recalled.dev/receipts/<event-id>. The page strips actor PII and metadata (the public viewer never sees who or what), and shows only:
- The action name
- The actor type (agent, user, service, etc.)
- The timestamp
- The hashes and signature
- A green or red banner whether the cryptographic proof verifies
Hand this URL out instead of dashboard access. Anyone can confirm an event existed without accessing your project.
Privacy
The public receipt page never exposes:
- Actor name, email, id, IP, user agent
- Event metadata
- Other events in the chain
It only proves that the specific event id, with that exact action verb and timestamp, was recorded by Recalled and has not been tampered with.