Skip to content

Verify the audit log's hash chains

The audit_log.verify operation: GET /api/v1/audit_log/verify in the REST API and the audit_log_verify tool in the MCP server.

GET/api/v1/audit_log/verify

Checks the organization's audit log hash chains now and returns their status. The log is append-only and hash-chained: each entry's hash covers the previous entry's hash and the entry itself, in 16 chains per organization (an entry's chain is chosen by its target). A changed, deleted, reordered or forged entry breaks its chain.

Each chain is checked over its newest 100 entries (links, hashes, the digests of the personal data kept outside the hash, and the chain's head); a shorter chain is checked from its first entry, and then complete is true. chains says, per chain, how many entries it holds and which positions verified (verifiedFrom to verifiedTo), or where it broke; brokenAt names the first break, in chain order (or entryId: null when the chain's head is the problem).

Every entry of every chain, including those older than your plan's visibility window, is also verified each night by Ownrecord, whose staff are alerted to a break (AUDIT_CHAIN_BROKEN). nightly is the latest of those verifications: when it ran, how many entries verified, and where it found a break (null before the first one). status is broken when the check now or the latest nightly verification found a break.

At a glance

  • REST: GET /api/v1/audit_log/verify
  • MCP tool: audit_log_verify
  • Operation ID: audit_log.verify
  • Scope: Organization
  • Permission: audit:read
  • Risk tier: read
  • Dry run: Not supported
  • Idempotency: Not needed (read only)
  • Modes: Test and live
  • Authentication: API key with the audit:read scope
  • Since: M10

Request

Query parameters

This operation takes no query parameters.

Response

200 OK with a JSON object:

FieldTypeDescription
object"audit_log_verification"
statusone of intact, broken
completeboolean
windowinteger (at least 1)
entriesinteger (at least 0)
checkedEntriesinteger (at least 0)
checkedAtstring
brokenAtobject or null
chainsarray of object
chains[].shardinteger (at least 0)
chains[].chainstring
chains[].entriesinteger (at least 0)
chains[].verifiedFrominteger (at least 0) or null
chains[].verifiedTointeger (at least 0) or null
chains[].statusone of intact, broken, empty
chains[].brokenAtstring or null
chains[].reasonstring or null
nightlyobject or null

Examples

An intact audit log

Every chain is longer than the window, so the newest 100 entries of each were checked.

HTTP
GET /api/v1/audit_log/verify
Authorization: Bearer or_test_…

Response (200):

JSON
{
  "object": "audit_log_verification",
  "status": "intact",
  "complete": false,
  "window": 100,
  "entries": 3720,
  "checkedEntries": 1600,
  "checkedAt": "2026-11-02T09:20:00.000Z",
  "brokenAt": null,
  "chains": [
    {
      "shard": 0,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3",
      "entries": 180,
      "verifiedFrom": 81,
      "verifiedTo": 180,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 1,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#1",
      "entries": 187,
      "verifiedFrom": 88,
      "verifiedTo": 187,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 2,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#2",
      "entries": 194,
      "verifiedFrom": 95,
      "verifiedTo": 194,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 3,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#3",
      "entries": 201,
      "verifiedFrom": 102,
      "verifiedTo": 201,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 4,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#4",
      "entries": 208,
      "verifiedFrom": 109,
      "verifiedTo": 208,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 5,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#5",
      "entries": 215,
      "verifiedFrom": 116,
      "verifiedTo": 215,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 6,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#6",
      "entries": 222,
      "verifiedFrom": 123,
      "verifiedTo": 222,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 7,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#7",
      "entries": 229,
      "verifiedFrom": 130,
      "verifiedTo": 229,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 8,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#8",
      "entries": 236,
      "verifiedFrom": 137,
      "verifiedTo": 236,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 9,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#9",
      "entries": 243,
      "verifiedFrom": 144,
      "verifiedTo": 243,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 10,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#10",
      "entries": 250,
      "verifiedFrom": 151,
      "verifiedTo": 250,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 11,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#11",
      "entries": 257,
      "verifiedFrom": 158,
      "verifiedTo": 257,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 12,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#12",
      "entries": 264,
      "verifiedFrom": 165,
      "verifiedTo": 264,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 13,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#13",
      "entries": 271,
      "verifiedFrom": 172,
      "verifiedTo": 271,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 14,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#14",
      "entries": 278,
      "verifiedFrom": 179,
      "verifiedTo": 278,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 15,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#15",
      "entries": 285,
      "verifiedFrom": 186,
      "verifiedTo": 285,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    }
  ],
  "nightly": {
    "status": "intact",
    "checkedAt": "2026-11-02T03:00:04.512Z",
    "entries": 3708,
    "brokenAt": null
  }
}

A break in older entries, found at night

The newest entries verify now, but last night's verification of every entry found a changed older entry.

HTTP
GET /api/v1/audit_log/verify
Authorization: Bearer or_test_…

Response (200):

JSON
{
  "object": "audit_log_verification",
  "status": "broken",
  "complete": false,
  "window": 100,
  "entries": 3720,
  "checkedEntries": 1600,
  "checkedAt": "2026-11-02T09:20:00.000Z",
  "brokenAt": null,
  "chains": [
    {
      "shard": 0,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3",
      "entries": 180,
      "verifiedFrom": 81,
      "verifiedTo": 180,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 1,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#1",
      "entries": 187,
      "verifiedFrom": 88,
      "verifiedTo": 187,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 2,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#2",
      "entries": 194,
      "verifiedFrom": 95,
      "verifiedTo": 194,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 3,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#3",
      "entries": 201,
      "verifiedFrom": 102,
      "verifiedTo": 201,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 4,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#4",
      "entries": 208,
      "verifiedFrom": 109,
      "verifiedTo": 208,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 5,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#5",
      "entries": 215,
      "verifiedFrom": 116,
      "verifiedTo": 215,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 6,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#6",
      "entries": 222,
      "verifiedFrom": 123,
      "verifiedTo": 222,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 7,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#7",
      "entries": 229,
      "verifiedFrom": 130,
      "verifiedTo": 229,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 8,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#8",
      "entries": 236,
      "verifiedFrom": 137,
      "verifiedTo": 236,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 9,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#9",
      "entries": 243,
      "verifiedFrom": 144,
      "verifiedTo": 243,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 10,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#10",
      "entries": 250,
      "verifiedFrom": 151,
      "verifiedTo": 250,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 11,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#11",
      "entries": 257,
      "verifiedFrom": 158,
      "verifiedTo": 257,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 12,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#12",
      "entries": 264,
      "verifiedFrom": 165,
      "verifiedTo": 264,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 13,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#13",
      "entries": 271,
      "verifiedFrom": 172,
      "verifiedTo": 271,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 14,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#14",
      "entries": 278,
      "verifiedFrom": 179,
      "verifiedTo": 278,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    },
    {
      "shard": 15,
      "chain": "org_01m4ttwd3veh9apbewxheq5xh3#15",
      "entries": 285,
      "verifiedFrom": 186,
      "verifiedTo": 285,
      "status": "intact",
      "brokenAt": null,
      "reason": null
    }
  ],
  "nightly": {
    "status": "broken",
    "checkedAt": "2026-11-02T03:00:04.512Z",
    "entries": 0,
    "brokenAt": {
      "entryId": "aud_01m2q8x7c1e9vbs3k0a5f7h2jd",
      "reason": "hash mismatch: the entry was changed after it was written"
    }
  }
}

Errors

CodeHTTP statusWhenWhat to do
permission_denied403You can't read the organization's audit log, or you're limited to some of its entities or brands (the chains are the whole organization's).Ask an organization owner or admin for a role or scope that allows this action.

It can also return these common errors: validation_failed, api_version_unsupported, authentication_required, api_key_invalid, resource_not_found, dry_run_not_supported, rate_limited, internal_error.