Verify the audit log's integrity
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
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.
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
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.
Inclusive end of the window. ISO 8601 datetime in UTC with a Z suffix.
Response
Verification result (not wrapped in data).
true when every signed row in the window matches its hash and links to its predecessor.
Redacted rows in the window with a valid attestation (exact count).
Ids of the first redacted rows (truncated list; the count above is exact).
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.
Id of the first broken row. Present only when valid is false. Inspect it with GET /api/audit/{id}.
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.

