Your logs are your word.
A receipt is evidence.
Your agents approve things, judge things, and act for your customers. When someone later disputes what happened, all you have is your own database — and there is no reason for them to believe it was not edited. AgentOath turns an action into a signed, timestamped record that anyone can check without trusting you, and without trusting us.
What that looks like in practice
A real receipt, stamped on 2026-08-20. Every line below was checked against a source that has nothing to do with this project.
# the receipt is signed — change one byte and the signature fails sha256(receipt) = b0a80f750c7021d8e107fa5c5227… # three unrelated timestamp authorities signed that hash with their own clocks 2026-08-20T19:48:15Z DigiCert SHA256 RSA4096 Timestamp Responder 2025 2026-08-20T19:48:15Z Certum Timestamp 2026, Asseco Data Systems S.A. 2026-08-20T19:48:16Z Apple Inc., Timestamp Signer NWK1 # 16 minutes later it was committed into the Bitcoin blockchain block 963342 mined 2026-08-20T20:04:12Z block 963346 mined 2026-08-20T20:32:06Z block 963374 mined 2026-08-21T02:10:47Z
That last part is the one that matters. Once a hash is in a Bitcoin block, proving it existed before that block needs no authority, no server, and no company that still has to be in business. It does not make our database tamper-proof. It makes our database irrelevant.
Why not just do this yourself
A fair question, and mostly the answer is that you can. Here is what you would end up building.
Signing is the easy part
Ed25519 is twenty lines. Then you discover Python prints 7.0 where JavaScript prints 7, your two services disagree on the canonical bytes, and every receipt one signs is rejected by the other — with no error, just "invalid". Three such rules, all of them silent.
The part that bites later
Receipts are public and permanent. The first time someone puts a customer email or an API key in one, you cannot take it back. A 28-key blocklist matched per underscore-segment stops it — and it has to catch prompt_hash and private_key_pem, which the obvious implementation does not.
The part you cannot do alone
A signature proves what. It cannot prove when — your own timestamp is worth exactly as much as your own log. That needs a party who is not you: a timestamp authority, or a blockchain. Both are wired in here.
None of it is novel. It is standard cryptography, RFC 3161, and OpenTimestamps — assembled, tested across two languages against byte-level fixtures, and honest about where the guarantees stop. That is the whole offer.
Three things, and only three
Identity, receipts, verification. Private task data never enters the Registry — a field whitelist and a key blocklist, on the client and again on the server.
Identity
A DID is derived from the public key: did:agentoath:sha256(pubkey). The same key is always the same identity, anyone can recompute it offline, and a new key is a new identity with no history.
Notarised receipts
Signed over canonical JSON that is byte-identical in Python and JavaScript. The Registry verifies the signature before storing anything and rejects what does not verify — including receipts we would rather keep.
Verification by anyone
The verify endpoint needs no key and stores nothing. Give it a receipt and a public key. It also checks that the DID really derives from that key, so a platform vouching for itself carries no weight here.
Sixty seconds
Disabled by default — nothing is sent until you turn it on.
# Python pip install "agentoath[timestamps]" from agentoath.hosted import Identity, build_signed_receipt from agentoath.timestamps import stamp_receipt, verify receipt = build_signed_receipt(identity, receipt_id="verdict-1234", action="compliance.verdict.red", metadata={"schema": "v1", "content_digest": sha256_json(text)}) proof = stamp_receipt(receipt) # only the hash leaves your machine // JavaScript npm install agentoath import { Identity, buildSignedReceipt } from 'agentoath/hosted';
Full integration guide — the three interop rules, the blocklist, and how to prove it worked →
Attesting documents
There is no document receipt type, which is the point: a document is an ordinary receipt plus one metadata convention, so signing, the cross-language canonical form, the blocklist and the Registry's validation all keep working unchanged.
The file stays with you
Only its sha256 goes into the receipt. A digest proves "this is the same file" to whoever already has it, and proves nothing to anyone who does not — which is what you want when the thing you are attesting is a scan you may not redistribute.
Idempotent by construction
The receipt id derives from the digest, so attesting the same bytes twice produces the same receipt rather than two unrelated records. Files are streamed — a multi-gigabyte scan does not have to fit in memory.
What it is for
Being able to answer "you had this on the date you claim" with something other than your own word. Provenance for training data, chain of custody for scanned material, evidence of what a compliance check actually said.
What this does not prove
🔴 Worth reading before you rely on it. The line between mathematics and trust is drawn in full on the integration guide; the short version:
Mathematics
A receipt's contents cannot be altered — not by anyone, including us. An identity cannot be impersonated. With a timestamp proof, the time cannot be backdated. All three are checkable by you, against us.
Still trust
That we do not delete a receipt from our database, and that a list you fetch is complete. There is no hash chain here and no append-only guarantee. Hold your own copy plus an OpenTimestamps proof and neither matters.
Why say so
Because the first person who checks will find out anyway, and an overclaim on this page would cost the parts that are true. If a claim here is not backed by something you can run, it should not be here.
API
Writes need X-API-Key; reads and verification are public, with rate limits. The machine-readable spec at GET /api/v1/registry is the authority for interop — this table is a summary.
| Endpoint | What it does |
|---|---|
| GET/api/v1/health | Service health |
| GET/api/v1/registry | Machine-readable spec — DID derivation, canonical JSON, signing rules. The authority for interop. |
| POST/api/v1/agents/register🔑 | Register an Ed25519 public key, get a did:agentoath DID (idempotent) |
| GET/api/v1/agents/{did} | Full agent record, including the public key |
| GET/api/v1/agents/{did}/card | Agent card: identity, capabilities, receipt totals |
| GET/api/v1/agents/{did}/status | Status, receipt count, average rating |
| GET/api/v1/agents/{did}/receipts | That agent's public receipts |
| POST/api/v1/receipts/publish🔑 | Publish a signed receipt — rejected outright if the signature does not verify |
| POST/api/v1/receipts/verify | Verify a receipt independently. No key, nothing stored, anyone can call it. |
| GET/api/v1/receipts/{receipt_id} | Look up a published receipt |