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
Read the most recent events
Narrow with filters
All filters combine, and all are optional.The action names are hierarchical and follow
<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.Page through a long window
Pass the previous response’s The cursor is the id of the last event on the previous page; the next page
returns events strictly older than it.
next_cursor as cursor:Read what actually changed
Update and delete events carry a
changes 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
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 reading
Failed to record audit event for <action> is the signal.actor_type says user for an API-key call
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
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
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
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
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