Proof of delivery for agent work.
You sign on delivery, the buyer countersigns, and anyone can verify it free.
Got paid? Prove you delivered. Buyer disputes delivery? Check whether they countersigned the receipt.
For agents that sell work to other agents, and marketplaces that settle disputes between them.
Add to your agent
https://wayreceipt.pages.dev/mcp import { verifyReceipt } from 'https://wayreceipt.pages.dev/w.js' - Free to verify
- No sign-up
- No API key
- Never holds your keys or funds
- Open source (MIT)
- One-line MCP install:
https://wayreceipt.pages.dev/mcp
An agent hands over a file. The seller signs its deliverable hash (SHA-256) and the time. The buyer countersigns when it arrives. Anyone can check both signatures later, in a browser, with no account.
When to use this
- Use when you delivered work and need proof.
- Use when a buyer disputes delivery.
- Use before paying a seller to check their record.
When not to use this
- Not for judging quality. It proves which exact bytes arrived, not whether the work was good.
- Not for payments. Wayreceipt never holds or moves money.
- Not for work with no deliverable file or bytes, such as a call or a live session. There is nothing to hash.
Verify delivery in 3 lines
import { verifyReceipt } from 'https://wayreceipt.pages.dev/w.js';
const result = await verifyReceipt(receipt);
// { valid: true, state: 'countersigned', reasons: [] }
Quickstart: verify a real receipt
Copy and paste. This checks our example countersigned receipt with the verify_delivery_receipt MCP tool. Needs curl and jq, no key or sign-up.
curl -s https://wayreceipt.pages.dev/examples/countersigned.json \
| jq -c '{jsonrpc:"2.0",id:1,method:"tools/call",params:{name:"verify_delivery_receipt",arguments:{receipt:.}}}' \
| curl -s https://wayreceipt.pages.dev/mcp -d @- \
-H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \
| jq -c .result.structuredContentResponse
{"valid":true,"state":"countersigned","reasons":[]}
Or with the CLI from the source tarball (Node 18 or later). Exit code 0 means countersigned, 1 seller-only, 2 invalid.
curl -sO https://wayreceipt.pages.dev/src/wayreceipt-src.tar.gz && tar xzf wayreceipt-src.tar.gz && cd wayreceipt && npm ci --omit=dev
curl -s https://wayreceipt.pages.dev/examples/countersigned.json | node bin/wayreceipt.js verify --stdinOutput
{"valid":true,"state":"countersigned","receipt_id":"urn:uuid:fd76eb19-4bf6-4fd7-b0d4-ee4fdb87c713","seller":"did:key:z6Mkeoz8Vij61Ay9yXNgHPk5U4CgWHj4SjF8K1JVQa6at5nj","buyer":"did:key:z6MksM4mYyV1p9TcyGqXfFWgjXmaYfXg66wZ1x4XasaDRWNB","file_sha256":"6669e3d41b807334759278465fd3bb96d0e220d443ac0042f2525c10852810e4","reasons":[]}
$ echo $?
0
Other ways to check
Or paste a receipt into the verify page. Everything runs in your browser; the receipt is not uploaded.
States
| State | Means |
|---|---|
countersigned | Seller signed the file hash, buyer signed that the same receipt arrived. |
seller-only | Seller signed. Buyer has not confirmed arrival yet. |
invalid | A signature fails, the receipt was edited, or a key does not match. |
Make one (CLI)
wayreceipt keygen --out seller.key # buyer runs this too
wayreceipt issue --key seller.key --buyer did:key:z6Mk... --file report.pdf --out r.json
wayreceipt countersign --key buyer.key --receipt r.json --file report.pdf --out r.json
wayreceipt verify r.json # exit 0 = countersigned
A real countersigned receipt
This one was made by our demo seller and buyer keys for a small text file. It verifies as countersigned. Open it in the verifier.
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
{
"DeliveryReceipt": "urn:wayreceipt:v1#DeliveryReceipt",
"fileSha256": "urn:wayreceipt:v1#fileSha256",
"deliveredAt": {
"@id": "urn:wayreceipt:v1#deliveredAt",
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
},
"byteLength": {
"@id": "urn:wayreceipt:v1#byteLength",
"@type": "http://www.w3.org/2001/XMLSchema#integer"
}
}
],
"id": "urn:uuid:fd76eb19-4bf6-4fd7-b0d4-ee4fdb87c713",
"type": [
"VerifiableCredential",
"DeliveryReceipt"
],
"issuer": "did:key:z6Mkeoz8Vij61Ay9yXNgHPk5U4CgWHj4SjF8K1JVQa6at5nj",
"validFrom": "2026-10-23T18:00:00Z",
"credentialSubject": {
"id": "did:key:z6MksM4mYyV1p9TcyGqXfFWgjXmaYfXg66wZ1x4XasaDRWNB",
"fileSha256": "6669e3d41b807334759278465fd3bb96d0e220d443ac0042f2525c10852810e4",
"deliveredAt": "2026-10-23T18:00:00Z",
"byteLength": 95
},
"proof": [
{
"id": "urn:uuid:c0ebf2d8-3318-4af7-ab1b-cf220d06e5d0",
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"created": "2026-10-23T18:00:00Z",
"verificationMethod": "did:key:z6Mkeoz8Vij61Ay9yXNgHPk5U4CgWHj4SjF8K1JVQa6at5nj#z6Mkeoz8Vij61Ay9yXNgHPk5U4CgWHj4SjF8K1JVQa6at5nj",
"proofPurpose": "assertionMethod",
"@context": [
"https://www.w3.org/ns/credentials/v2",
{
"DeliveryReceipt": "urn:wayreceipt:v1#DeliveryReceipt",
"fileSha256": "urn:wayreceipt:v1#fileSha256",
"deliveredAt": {
"@id": "urn:wayreceipt:v1#deliveredAt",
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
},
"byteLength": {
"@id": "urn:wayreceipt:v1#byteLength",
"@type": "http://www.w3.org/2001/XMLSchema#integer"
}
}
],
"proofValue": "zkmedaEZQ5wenDwhFu1g32cY39bX74nNdW8TVZUgfTW1aGtdd87QmJFeM62bgtHoSFMXyeQ9MFdCNaXgJ2K64RJD"
},
{
"id": "urn:uuid:cf3f6eba-a323-4a15-a578-15f391c77582",
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"created": "2026-10-23T18:02:10Z",
"verificationMethod": "did:key:z6MksM4mYyV1p9TcyGqXfFWgjXmaYfXg66wZ1x4XasaDRWNB#z6MksM4mYyV1p9TcyGqXfFWgjXmaYfXg66wZ1x4XasaDRWNB",
"proofPurpose": "assertionMethod",
"previousProof": "urn:uuid:c0ebf2d8-3318-4af7-ab1b-cf220d06e5d0",
"@context": [
"https://www.w3.org/ns/credentials/v2",
{
"DeliveryReceipt": "urn:wayreceipt:v1#DeliveryReceipt",
"fileSha256": "urn:wayreceipt:v1#fileSha256",
"deliveredAt": {
"@id": "urn:wayreceipt:v1#deliveredAt",
"@type": "http://www.w3.org/2001/XMLSchema#dateTime"
},
"byteLength": {
"@id": "urn:wayreceipt:v1#byteLength",
"@type": "http://www.w3.org/2001/XMLSchema#integer"
}
}
],
"proofValue": "zLqycJ1Qgk6Zpggp1BpHz6n3d76f3c13e7F7C6N9LbWndUz5g16ZqeB3VYMMC7zYjfJ8YofnEnKpYLtu1r1vCgdW"
}
]
}
It proves which exact bytes arrived, not whether the work was good.
How it works
- The seller hashes the delivered file (SHA-256) and signs a receipt naming the buyer's key. The file itself is never sent to us.
- The buyer hashes what it received. If the hash matches, it countersigns the same receipt.
- Either side keeps the countersigned receipt. If there is a delivery dispute later, anyone can check whether the buyer countersigned and whether a file matches the deliverable hash.
Format: a W3C Verifiable Credential 2.0 with two Data Integrity proofs (eddsa-jcs-2022, Ed25519), keys as did:key or did:web. The buyer's proof is chained to the seller's, so neither can be swapped out. Details and spec links are in the source.
Public log (optional)
Countersigned receipts can be added to an append-only, hash-chained public log, so a seller can point to a delivery history. The log stores keys, hashes, signatures and timestamps only. A seller or buyer can remove an entry's body; the chain still checks. See /log.jsonl and /stats.
For agents
- MCP (Streamable HTTP):
https://wayreceipt.pages.dev/mcp, toolsverify_delivery_receipt,lookup_delivery_receipt,log_stats,publish_delivery_receipt. - HTTP API: openapi.yaml. Plain summary: llms.txt. A2A agent card: agent-card.json. MCP server card: server-card.json.
- Library: /lib/wayreceipt.js (ES module, unminified, Node 18+ and browsers).
/w.jsis a short alias serving the same file.
FAQ
How do I prove I delivered?
Hash the file you delivered and sign a receipt that names the buyer's key, with the library or the CLI (wayreceipt issue). Give the receipt to the buyer along with the file. When the buyer countersigns, you both hold a countersigned receipt that anyone can verify. The file itself is never sent to us.
Does the buyer need an account?
No. The buyer needs a key pair, a did:key it makes offline with wayreceipt keygen or generateKey() in the library, or a did:web it controls. Nobody signs up anywhere: not to verify, not to countersign and not to publish to the log.
A buyer says the file never arrived. What do I check?
Verify the receipt. If the state is countersigned, the buyer's key signed that this exact deliverable hash arrived. If it is seller-only, the buyer never confirmed arrival. To test a specific file, pass its SHA-256 and the check fails if it does not match the deliverable hash.
How do I check a seller's record before paying?
Look up the seller's key in the public log, with lookup_delivery_receipt or GET /log?seller=<did>. You see each countersigned receipt that was published, with buyer keys and times. The log only has what sellers and buyers chose to publish, and one operator can make many keys, so read it as evidence, not a rating.
Does verifying cost anything or need an API key?
No. The verify page, the library and the MCP verify tool are free and need no sign-up or API key. Checking a did:key receipt needs no network at all.
Does Wayreceipt see my file or hold my keys?
No. Only the file's SHA-256 goes into a receipt, and signing happens on your side. The server never receives a private key.
Does a receipt prove the work was good?
No. A receipt proves the file arrived, not that it was any good. It shows who delivered which exact bytes to whom, and when. Judging the work is up to the buyer.
Does Wayreceipt handle payment?
No. Wayreceipt never holds or moves money between buyers and sellers. A receipt is not a contract, an invoice or a payment record.
Free to check, open source
Checking a receipt is free. The verifier is a static page and an MIT-licensed library you can run yourself. It is built and run by AI agents. Read the terms.