/api/v1/compliance/ledger-checkpointCurrent signed evidence-ledger checkpoint (org-scoped, freshness-signed length anchor)
Publishes the organization's CURRENT signed evidence-ledger checkpoint — the length anchor that makes tail truncation detectable. A hash chain commits to ORDER, never LENGTH: lop N records off a signed chain and every survivor still has contiguous seq, intact prevHash linkage and a valid signature. Only a signed commitment to (seq, head recordHash) makes that visible. FRESHNESS IS INSIDE THE SIGNATURE (payload v2). The signed body is `{v:2, orgId, seq, recordHash, keyId, issuedAt, nonce}`. `issuedAt` is the mint instant and `nonce` is the challenge the VERIFIER sent as `?nonce=`, echoed back — both covered by the Ed25519 signature. The verifier enforces a maximum signed age and requires the challenge echo, so a replayed response is refused even when it is a genuine one. This matters concretely: the v1 payload signed only `{orgId, seq, recordHash, keyId}` and carried `issuedAt` alongside it as unsigned transport metadata, which nothing ever compared to anything — so a publisher replaying its own genuine six-year-old checkpoint satisfied the live fetch and the CLI certified an export with 4 of 5 records deleted, exit 0, nothing forged. A checkpoint minted before v2 has no signed timestamp and cannot acquire one retroactively; verifiers report those as `checkpoint-freshness-unsigned` and never certify them. DO NOT SAVE THIS TO A FILE AND PASS IT AS `--checkpoint`. A file anchor can FAIL an export but can never certify one (`completeness.reason: "offline-anchor-only"`, exit 1): a checkpoint is a LOWER bound, a fresh one is minted on every append, a file cannot answer a live challenge, and in practice it reaches the auditor from the party being audited. Have the verifier fetch it instead: `evalguard verify export.json --public-key ledger.pub.pem --checkpoint-url https://evalguard.ai --checkpoint-token <key>`, or `verifyEvidenceExportLive()` from `@evalguard/core/verify`, both of which mint a fresh 128-bit challenge per run. A fetch that fails for any reason is reported as CANNOT-PROVE-COMPLETE and exit 1, never as a pass and never as a fallback to the checkpoint travelling inside the export. AUTHENTICATION: an org-scoped credential is required. A customer issues their third-party auditor a read-only, revocable API key (`compliance:read` suffices); API keys are pinned to their own organization, so a credential can only ever retrieve that one tenant's anchor. This does not weaken the anchor: every response is signed over the LIVE database head, so the organization can choose whether you reach this endpoint but never what it says — and a refusal fails closed in the verifier as an explicit finding rather than a silent pass. (This endpoint was briefly unauthenticated; that posture is withdrawn. It gave anyone holding an orgId — which appears in every export, audit bundle and dashboard URL, and cannot be rotated — a permanent, unrevocable read of any tenant's evidence volume, and bought nothing for the anchor's integrity.) AVAILABLE ON EVERY PLAN TIER. Although it lives under the Team+ feature-gated /api/v1/compliance prefix, this path is exempt from the plan/feature entitlement gate. Being able to prove that evidence we already produced has not been truncated is not a paid add-on: a Free or Pro customer's auditor must be able to obtain the anchor, because a verifier with no anchor fails closed and certifies nothing. The credential requirement is unchanged. The paid compliance product surface (reports, scores, frameworks, controls) remains gated. DISCLOSURE, stated plainly: `length` (= `checkpoint.seq + 1`) IS the organization's exact cumulative evidence-record count, and because the anchor is minted live and never cached, a caller who polls observes both that count and its rate of change — how much governance evidence the organization produces, and when. This is inseparable from the artefact (a length anchor whose length is secret is not an anchor) and `seq` sits inside the signed body, so it cannot be withheld. It is the reason a revocable credential is required rather than optional. Beyond that count and the commitment itself, the response carries no evidence content, no record bodies, no argHash, no prevHash, no non-head recordHash, no metadata, no PII and no key material. Note also that 200/503 versus 404 does distinguish 'this organization holds evidence' from 'empty' — a real distinction, though only visible to a caller who already holds that organization's credential. READ-ONLY: this path never provisions. An organization with no active signing key receives 503 LEDGER_SIGNER_UNPROVISIONED; signing keys are created only on an authenticated evidence append. Rate limit: 10 requests/minute.
Authentication
Send Authorization: Bearer YOUR_API_KEY on every request. Generate API keys at /dashboard/settings/api-keys.
Parameters
orgId in queryrequiredOrganization whose ledger head to commit to, and which your credential must be scoped to. Must be a UUID; 400 INVALID_ORG_ID otherwise.
stringnonce in queryLiveness challenge, echoed verbatim INSIDE the signature as `checkpoint.nonce`. Generate 128 bits of fresh randomness per verification and never reuse one; the verifier requires the echo before it will certify completeness, which is what makes a retrieved anchor distinguishable from a recording of one. Omitting it yields a checkpoint that is still signed and still fresh but cannot certify (`checkpoint-unchallenged`). 400 INVALID_NONCE when malformed — a mangled challenge is rejected rather than signed, because a failed echo is indistinguishable from a replay.
stringResponse
All status codes
Code samples
cURL
curl -X GET \ https://evalguard.ai/api/v1/compliance/ledger-checkpoint \ -H "Authorization: Bearer $EVALGUARD_API_KEY"
TypeScript
// The TypeScript SDK (@evalguard/sdk) exposes TYPED methods — runEval,
// getEval, runSecurityScan, checkFirewall, … — not a generic request().
// For an arbitrary endpoint, call it directly:
const res = await fetch("https://evalguard.ai/api/v1/compliance/ledger-checkpoint", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.EVALGUARD_API_KEY}` },
});
console.log(res.status, await res.json());Python
# The Python SDK (pip install evalguardai) exposes TYPED methods on
# EvalGuardClient — run_eval, get_eval, … — not a generic request().
# For an arbitrary endpoint, call it directly:
import os
import requests
headers = {"Authorization": f"Bearer {os.environ['EVALGUARD_API_KEY']}"}
response = requests.request("GET", "https://evalguard.ai/api/v1/compliance/ledger-checkpoint", headers=headers)
print(response.status_code, response.json())Go
package main
import (
"context"
"fmt"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequestWithContext(context.Background(), "GET", "https://evalguard.ai/api/v1/compliance/ledger-checkpoint", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("EVALGUARD_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
fmt.Println(resp.Status)
}