Event signatures
Cryptographically prove that captured events haven't been tampered with using Ed25519 signatures and Merkle digests.
Each event can be signed with your organization's Ed25519 private key at capture time. TokenID stores the signature alongside the event and periodically computes a Merkle root over each window — giving you per-event and per-batch proof that nothing has been altered. Useful for compliance evidence, dispute resolution, and auditor handoff.
How it works
- The SDK signs each event with your org's Ed25519 private key before it leaves the process,
attaching the signature, a per-event nonce, and a
signed_attimestamp. - The backend stores those fields on the row. Any later read can be re-verified against the public key registered for your org.
- Periodically a Merkle root is computed over every event in a time window and (optionally) pushed to your webhook. The root, window bounds, and row count are stored immutably.
Register a signing key
/api/v1/signing-keysIdempotent — re-registering the same key returns the existing record. The fastest path is the CLI helper, which generates a keypair and posts the public half:
tokenid keygen --register
Or manually:
curl -X POST https://token.audit.id/api/v1/signing-keys \
-H "Authorization: Bearer td_live_xxxx" \
-H "Content-Type: application/json" \
-d '{"public_key": "MCowBQYDK2VwAyEA…", "algorithm": "ed25519", "label": "prod-2026-05"}'
| Field | Type | Notes |
|---|---|---|
public_key |
string | 64-char hex OR base64 of 32 raw Ed25519 bytes |
algorithm |
string | Must be "ed25519" (only supported value) |
label |
string | Optional, max 128 chars |
The response carries the signing_key_id — the SDK passes this back on every signed event so
the backend knows which key to verify against.
Verify a single event
/api/v1/events/{event_id}/verifyRe-runs the Ed25519 verification against the canonical event fields and the stored signature.
{
"event_id": 482910,
"has_signature": true,
"verified": true,
"key_fingerprint": "9f3c1ab2…",
"message": "Signature valid."
}
If the event was ingested before signing was enabled, has_signature is false. If the row
was tampered with after ingest, verified returns false with "Signature INVALID — event
data may have been tampered.".
Digest webhooks
Subscribe an HTTPS endpoint to receive each Merkle digest as it's signed.
Digest history
Two endpoints expose stored digests:
| Endpoint | Use case |
|---|---|
GET /api/v1/org/{org_id}/digests |
Simple list, most recent first, up to limit=500 |
GET /api/v1/org/{org_id}/digest-history |
Paginated, supports since / until, returns total |
Each digest row carries:
| Field | Description |
|---|---|
window_start / window_end |
Time bounds of the events covered |
merkle_root |
Hex root over canonical leaves of every event in the window |
row_count |
Number of events folded into the root |
tokenid_signature |
TokenID's own signature over the digest (countersigned) |
delivered_at |
When the digest was pushed to your webhook (null if pending) |
Verify a batch
/api/v1/org/{org_id}/digest/verifyThe auditor's primary tool. Given a digest_id and a list of event_ids claimed to have been
in that window, the endpoint recomputes the Merkle root from every event in the window and
checks (1) that the recomputed root matches what was stored, and (2) that every requested event
was in the window.
curl -X POST https://token.audit.id/api/v1/org/{org_id}/digest/verify \
-H "Authorization: Bearer td_live_xxxx" \
-H "Content-Type: application/json" \
-d '{
"digest_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"event_ids": [482910, 482911, 482912]
}'
{
"digest_verified": true,
"events_included": true,
"digest_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"window_start": "2026-05-20T00:00:00Z",
"window_end": "2026-05-20T01:00:00Z",
"stored_root": "f4c2…",
"computed_root": "f4c2…",
"window_event_count": 1842,
"requested_events_found": 3,
"events_requested": 3,
"message": "Digest integrity verified and all requested events confirmed in window."
}
See Auditor export for the JSONL companion endpoint that hands an auditor every event in a date range, ready to be verified against the digests above.