For sellers, and for anyone reviewing a record on a seller's behalf.
When a Caddie seller grants our support team temporary access to their account, every action taken inside that account is written to a record the seller can read and download. This page explains how to check that record independently, without our help and without needing to trust us.
That last part is the point. A record that only its keeper can vouch for is not much of a record.
One entry is written for every action Caddie Support takes inside a seller's account: what was changed and from what value to what value, items created or deleted with all their fields, bulk operations with the affected item ids, and attempts that were refused by our own restrictions, such as anything touching billing, passwords or API keys. Refused attempts are recorded on purpose. A record containing only what succeeded has a hole in it exactly where someone would want to look.
Ordinary reads are not all recorded, and machine keepalives are excluded. Entry into the account is always recorded.
pending in your record and carries no Hedera timestamp. See "Checking the fingerprint against Hedera" below.The fingerprint is a SHA-256 digest, lowercase hex, of a single UTF-8 string. That string is a JSON object with exactly these nine fields, keys sorted alphabetically, no whitespace between tokens:
| Field | Meaning |
|---|---|
actor | Always Caddie Support. Support is recorded as the company, not as an individual. |
created_at | UTC, ISO 8601, no timezone suffix. Microseconds are omitted entirely when zero. |
grant_id | The specific permission the seller gave, under which this action was taken. |
method | HTTP method. |
route | The route template, not the concrete URL. |
status_code | HTTP status. 403 means our own restrictions refused the attempt. |
summary | The plain-language description of what happened. |
target_id | The affected record, or null. |
user_id | The seller's account. |
You do not need to build this string yourself. Every entry in the downloaded file already contains it verbatim, as canonical_payload. Rebuilding it by hand is the one way to get a wrong answer, so the file hands you the exact bytes instead. The format is documented here so you can confirm we are not hiding anything in it, not so you have to reproduce it.
An entry recording a price change from $40.00 to $25.00. This is the exact canonical_payload string:
{"actor":"Caddie Support","created_at":"2026-08-09T18:52:14.203871","grant_id":"7c1f0f4e-6a2b-4f13-9c8d-2a5b7e91d004","method":"PUT","route":"/api/inventory/{item_id}","status_code":200,"summary":"Edited an item: price_cents 4000 -> 2500","target_id":"e58c2f90-1d34-4b77-8a05-6f2c9b13de47","user_id":"b3d9a1c2-5e47-4a8f-9d21-0c6f4b7e2a38"}
Its SHA-256 digest, which is the content_hash stored on that entry:
21d38eb08759e8e61c204813063f73ee4e395d61b8b99754d4e9735fb4afa500
printf '%s' 'PASTE_THE_canonical_payload_HERE' | shasum -a 256
Use printf rather than echo. echo adds a trailing newline on most systems and will give you a different digest.
import hashlib, json
doc = json.load(open("caddie-support-record.json"))
for grant in doc["grants"]:
for a in grant["actions"]:
if not a["verifiable"]:
print("SKIP (no checkable text stored):", a["summary"])
continue
digest = hashlib.sha256(a["canonical_payload"].encode("utf-8")).hexdigest()
status = "OK" if digest == a["content_hash"] else "MISMATCH"
print(status, a["at"], a["summary"])
Every entry should print OK. A MISMATCH means that entry's contents differ from what was fingerprinted when it was written.
An entry is fingerprinted first and published second. Publishing never delays or blocks a seller's own data, so an entry can exist and be checkable before its fingerprint has reached the topic. Entries awaiting publication are marked pending in the downloaded record and carry no sequence number, no consensus timestamp and no mirror node link. A pending entry still passes the first check above. It simply has no independent timestamp yet.
Fingerprints are submitted to a single Hedera Consensus Service topic on mainnet:
| Network | Hedera mainnet |
|---|---|
| Topic | 0.0.10802877 |
| Browse | hashscan.io/mainnet/topic/0.0.10802877 |
| Raw messages | public mirror node |
To check one specific entry, take its anchor.mirror_node_url from the downloaded record and open it. The message field is base64. Decode it, and it must equal that entry's content_hash exactly. The consensus_timestamp in that response is the network's own, to the nanosecond, and must match the entry's anchor.consensus_timestamp digit for digit. We publish the timestamp in the network's exact form rather than a rounded one, so there is nothing to reconcile.
The topic was created with no admin key. On Hedera that is permanent and publicly checkable: the topic can never be modified or deleted by anyone, including Caddie, and including if we were ordered to. You can confirm this yourself. Open the raw link above and look for "admin_key": null. Anyone may also pay to extend the topic's expiration, so a seller or their lawyer can keep this evidence alive without our cooperation.
This record is tamper-evident. It is not tamper-proof, and we will not describe it that way.
We would rather state the limit plainly than let a seller or a court discover it later.
If an entry shows MISMATCH, or an entry you expected is missing, contact support@getcaddie.now. Keep your downloaded file. It is your copy and it does not depend on us continuing to hold anything.
Canonical form v1. If this format ever changes, it will be published as a new version anchored to a new topic, never edited in place, because changing it would invalidate every fingerprint already published under it.