Skip to navigation

Verify the audit log's integrity

View as Markdown

Recomputes the workspace’s tamper-evident hash chain and reports the first row that does not match. Each audit row stores row_hash, a hash of its content and of the previous row’s hash. Changing, inserting or deleting a row in the middle breaks the chain from that point on.

Rows are walked oldest first (created_at, then id) inside the optional [from, to] window, both ends inclusive. Without a window, the whole retained history is checked, which can take a few seconds on large workspaces. Rows older than the hash chain (no row_hash) are skipped.

Redacted rows are accepted when their redaction attestation (redaction_hmac) is valid. That covers GDPR erasure of changes and the 90-day IP/user-agent sweep. They are counted in redactedRows, so a green result still tells you a redaction happened. anchoredMidChain: true is normal with a from bound, or after retention deleted older rows: the first row links to one outside the window, and that link was not checked.

The result is returned at the top level, not wrapped in data.

Idempotency. A read with no side effects. Safe to retry. The result only changes when new rows are written or a redaction happens.

Access

  • Required scope: full. Human users need the owner role; admins are refused.
  • Rate limit: Audit — 20 requests/min per workspace, shared with the other audit routes, plus the general API limit (120–600/min by plan). See Rate limits.
  • Plan: Available on every plan.

Authentication

AuthorizationBearer

Workspace API key: jl_ followed by 64 lowercase hex characters, created by the workspace owner in the dashboard (Settings → API Keys) and sent as Authorization: Bearer jl_.... The plaintext is shown once, at creation; Jelliu stores only a SHA-256 hash. A workspace can hold up to 25 active keys.

ScopeGET / HEADPOST / PUT / PATCH / DELETEAdmin-only routes
readYesNoNo
writeYesYesNo
fullYesYesYes

Operations restricted to admins or owners reject keys without the full scope with 403, and say so in their description. No key, whatever its scope, can mint or revoke API keys or rotate a webhook secret — that requires a signed-in owner session. A revoked key stops authenticating within about 10 seconds. See Authentication.

Query parameters

fromdatetimeOptional

Inclusive start of the window. ISO 8601 datetime in UTC with a Z suffix; an offset such as -05:00 or a bare date answers 400.

todatetimeOptional

Inclusive end of the window. ISO 8601 datetime in UTC with a Z suffix.

Response

Verification result (not wrapped in data).

validboolean

true when every signed row in the window matches its hash and links to its predecessor.

totalCheckedinteger
Rows examined in the window.
redactedRowsinteger

Redacted rows in the window with a valid attestation (exact count).

redactedRowIdslist of strings

Ids of the first redacted rows (truncated list; the count above is exact).

anchoredMidChainboolean

The first signed row in the window links to a row outside it (normal with a bounded from or after retention); that link was not checked.

firstInvalidRowstringOptionalformat: "uuid"

Id of the first broken row. Present only when valid is false. Inspect it with GET /api/audit/{id}.

reasonenumOptional

Why the first broken row failed. Present only when valid is false. hash_mismatch: the row’s content was changed after it was written. link_broken: the row does not point at the previous row’s hash (a row was inserted or deleted). unattested_redaction: the row is marked redacted but the redaction signature is missing or invalid.

Allowed values:

Errors

400
Bad Request Error
401
Unauthorized Error
403
Forbidden Error
429
Too Many Requests Error