Read the system-tenant evidence ledger (cross-tenant ops; superadmin only)
const url = 'https://example.com/v1/audit/system?from=1&limit=50';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://example.com/v1/audit/system?from=1&limit=50' \ --header 'Authorization: Bearer <token>'Reads the system-tenant evidence ledger, which records the cross-tenant operations. Superadmin only.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Query Parameters
Section titled “Query Parameters ”Start from this sequence number. The page runs FORWARDS from it, in ascending sequence order; pair it with head_seq to address the newest events.
Maximum number of items to return.
Omit events whose action starts with this prefix. Repeatable: give it once per action family to leave out. It uses the SAME prefix rule as action, and it filters only what is RETURNED — the ledger still records every event, and a request without this parameter still returns them all. Its use is a caller that must not be shown its own footprint: the console’s notification bell passes exclude_action=audit.read so that reading the ledger does not itself become the newest activity in it.
Responses
Section titled “ Responses ”OK
object
The highest sequence number this tenant’s ledger has RECORDED, and 0 when it has never recorded one. from pages FORWARDS (events come back in ascending sequence order), so this is the only field that addresses the END of the chain: request from=max(1, head_seq-N+1)&limit=N and reverse the page to show the newest activity. Read the two bounds of that promise exactly, because a caller who assumes more will be wrong on a real ledger. (1) The window is N SEQUENCE POSITIONS, not N rows: a chain that declares a gap (an in-chain audit.gap marker) skips positions, so the page can come back SHORTER than N with older events still present below it. (2) head_seq is measured before this request’s own self-audit event joins the chain, and it is never behind the highest sequence in items — but it does not identify the exact snapshot the page was read from, because a concurrent append can land between the two reads. It is the RECORDED tip, which on a ledger emptied under a live head is deliberately not the last addressable row.
object
Hex-encoded SHA-256 of this event
Hex-encoded SHA-256 of the previous event
Base64-encoded Ed25519 signature
Example generated
{ "cursor": "example", "has_more": true, "head_seq": 1, "items": [ { "action": "example", "actor": "example", "actor_kind": "example", "hash": "example", "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "occurred_at": "2026-04-15T12:00:00Z", "prev_hash": "example", "seq": 1, "sig": "example", "target_id": "example", "target_kind": "example" } ]}Bad request
object
object
Example generated
{ "error": { "code": "example", "message": "example" }}Unauthenticated
object
object
Example generated
{ "error": { "code": "example", "message": "example" }}Forbidden
object
object
Example generated
{ "error": { "code": "example", "message": "example" }}Not found
object
object
Example generated
{ "error": { "code": "example", "message": "example" }}Conflict / setup required
object
object
Example generated
{ "error": { "code": "example", "message": "example" }}Rate limited
object
object
Example generated
{ "error": { "code": "example", "message": "example" }}