API reference
Verify the ledger
/ api/ v1/ ledger/ verifyReplays signatures, body hashes and hash-chain continuity for the workspace the calling key belongs to.
valid has three values, not two. true verified and false broken are findings about the chain; null means no verdict was produced, because there was no key to check authorship against. reason says which case it is. A key that is stored but cannot be read is a configuration problem, reported in warnings, not a finding about the chain.
Send a workspace API key as Authorization: Bearer ….
Parameters
No parameters.
Try it
The key is kept in memory only, for this page.
Code samples
curl "https://www.vestiarion.xyz/api/v1/ledger/verify" \
-H "Authorization: Bearer $VESTIARION_API_KEY"Response
Example{
"data": {
"valid": true,
"checkedEntries": 99
}
}dataobjectThe verdict of replaying the workspace's ledger: signatures, body hashes and hash continuity.
5 fields in data
validboolean · nullabletrueverified,falsebroken, andnullnot checked, which is a third answer, not a soft failure. A workspace holding no public key has produced no evidence either way.checkedEntriesnumberbrokenAtnumber · optionalThe
seqof the first entry that failed. Absent unlessvalidisfalse.reasonstring · optionalWhy the verdict is what it is, when it is not a plain
true.warningsarray of string · optionalConfiguration problems found on the way to this verdict; not about the chain.
Errors
| Status | Code | When |
|---|---|---|
| 401 | unauthorized | No key, or a malformed, unknown or revoked one: "A valid API key is required." |
| 403 | forbidden | The key's scopes do not cover this route: "This key cannot do that." Or, on a write, the person who created the key can no longer add records: "This key's issuer can no longer add records in this workspace." |
| 500 | internal | An unexpected server error. Implementation details are not exposed. |
Notes
valid has three values
true (verified) and false (broken) are findings about the chain. null means no verdict was produced: the workspace has no ledger public key to check against, so authorship was never checked. A consumer that treats null as a failure will report a tampered audit trail when a key was never set up for the workspace. reason says which case it is, and brokenAt is absent whenever valid is null.
{"data":{"valid":null,"checkedEntries":99,"reason":"no ledger public key is configured, so authorship was not checked"}}A key that cannot be read
A key that is stored but cannot be read, such as a PEM whose newlines were lost, is a configuration problem, not a finding about the chain, and the two are kept apart. Verification never fails over a bad key: it verifies with whatever it could read, and reports what it could not in warnings:
{"data":{"valid":true,"checkedEntries":105,"warnings":["The ledger signing key is not a readable private key: error:1E08010C:DECODER routines::unsupported"]}}When a broken key is the reason no key is available at all, reason names it, instead of saying "no ledger public key is configured".
Key identity and rotation
Every entry written since signing key ids were introduced carries signingKeyId; older entries carry null. Verification accepts a keyring: the workspace's current key plus the earlier public keys the deployment still accepts. A labeled entry is checked against the key it names, and an unlabeled one against any key in the ring. So a rotated key keeps the history it signed verifiable, and a label the ring does not know is reported as its own case, again with valid: null:
{"data":{"valid":null,"checkedEntries":112,"reason":"entry #104 was signed by key 4f2a9c1e88b30d57, which is not in this deployment's keyring (a71b0e6640cc2f93), so its authorship was not checked"}}That message names the missing key and the ones the deployment has: the difference between a suspected forgery and a retired key that was never added to the keyring.
A rotation is recorded in the ledger itself, as a system entry with action: "ledger_key_rotated", signed by the new key. When an owner rotates the key from Settings, the entry has actor: "human" and its detail is {"from": "<old id>", "to": "<new id>", "by": "<user id>"}, where by is the owner who rotated. When the key changed any other way, the first append under the new key writes the entry automatically, with actor: "system" and detail {"from": "<old id>", "to": "<new id>"}. Either way, a consumer following the ledger sees the change of authority as an entry.
From that entry on, signingKeyId is the new key's id. Earlier entries keep the old id, and keep verifying against the retired key, which stays in the workspace's keyring.
The public key
This endpoint reports whether the chain checks out; it does not return the public key. To check signatures yourself, take the PEM and its key id from the workspace's Audit page, /o/<slug>/audit, under "Ledger signing public key". Verifying a ledger entry's signature shows how.
The Audit page itself calls a separate route, GET /api/ledger/verify?org=<slug>, which takes a signed-in member's session rather than an API key and answers in a legacy bare shape. It is not part of the v1 API; integrations use this endpoint.