Review the audit trail
Do this to answer “who changed this, when, and from where” about configuration: agents, skills, MCP servers, triggers, tasks and policy rules. The audit log records the change, the actor, the request context and a field-level diff. Do not use it to reconstruct what an agent did while running. Tool calls, denials, budget warnings and approvals are in the task event stream, not in the audit log — the two are separate stores. If your question is about a run, skip to the second trail.Prerequisites
- An authenticated session in the workspace you want to inspect. Reads are workspace-scoped, and any authenticated member can read their workspace’s full audit log.
- Read audit for what each trail covers.
API=http://localhost:8000 and a bearer token in $TOKEN.
Steps
1. Read the most recent events
2. Narrow with filters
All filters combine, and all are optional.<resource>.<verb>. The set written
today is agent, skill, mcp_server, mcp_instance and trigger create/update/delete,
task.create, and governance_policy.create, .update, .set_enabled and
.delete.
3. Page through a long window
Pass the previous response’snext_cursor as cursor:
4. Read what actually changed
Update and delete events carry achanges array of {field, before, after}:
created_at and updated_at are excluded from diffs. Create events have no
diff, because there is no before-state.
source_ip comes from X-Forwarded-For when present and the direct client
otherwise, so it is only as trustworthy as your proxy configuration.
request_id is the inbound X-Request-ID header, or a generated UUID when the
caller did not send one — send your own to correlate with upstream logs.
Verify
Make a change you can predict, then find it:governance_policy.create with your user id as
actor_id, timestamped within seconds of the call.
When the audit log has no answer
Several things are deliberately or accidentally absent. Knowing which is which saves an investigation. Runtime governance decisions are not there. No tool call, policy denial, budget denial or approval produces an audit row. The pipeline’s audit observer is registered but constructed without an event sink, so it logs at debug level and writes nothing. Use the task event stream instead:tool.call, tool.result (including denied_by_policy),
llm.call.*, approval.request, approval.response, BudgetWarning,
BudgetExceeded and the terminal states.
Authorization grant changes are not there. Writing or revoking a relationship
through /v1/access-control/relationships, and the owner grants written
automatically when a resource is created, produce no audit event. Who has access
is recorded in the graph itself — read it with
GET /v1/access-control/relationships and POST /v1/access-control/resolve.
API key lifecycle is not there. Creating and deleting keys under
/v1/api-keys/ writes no audit event.
Troubleshooting
An action you expected is missing. Check it is one of the covered actions above. Beyond coverage, two mechanics drop events silently: the decorator skips auditing when the service has no repository factory, and it catches and logs failures from the audit write so the mutation still succeeds. A warning in the API log readingFailed to record audit event for <action> is the signal.
actor_type says user for an API-key call. The column exists to
distinguish user, service, system and api_key, but no call site sets it,
so everything is recorded as user with the resolved user id. You cannot tell
interactive from programmatic activity from this field. Use user_agent and
source_ip as a weaker proxy.
limit=500 returned 100 rows. The parameter is validated to 1-100 and the
repository clamps it again. Page with cursor.
Events stop at a certain date. There is no retention or archival job in core,
so this is not expiry — check whether the workspace filter is what you expect.
Reads are scoped to the caller’s current workspace, and switching workspaces
changes the result set entirely.
You need the log somewhere else. Core has no export endpoint. Enterprise
deployments can register an audit_sink extension that receives every recorded
event for SIEM or object-storage delivery; a forwarding failure is logged and
does not fail the write.
You are relying on immutability. The repository only inserts and reads, and
the table is documented as append-only, but nothing in the application enforces
that. Grant only INSERT and SELECT on audit_events at the database level if the
guarantee matters.
Related
- Audit — the two trails and why they are separate.
- Grant access to a resource — grant changes, which this log does not record.
- Require human approval — approval outcomes, which live in the event stream.