Read the tenant evidence ledger
const url = 'https://example.com/v1/audit?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?from=1&limit=50' \ --header 'Authorization: Bearer <token>'Reads a page of the tenant evidence ledger. Paging is keyset by SEQUENCE, not a cursor, and the page runs FORWARDS from ?from in ascending sequence order — oldest first. The response carries head_seq, the chain tip, which is what a caller pairs with ?from to address the newest events.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Header Parameters
Section titled “Header Parameters ”Target tenant id; required when the principal can act in more than one tenant.
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" }}