How to check a signed receipt before you rely on it, step by step with curl
Last updated: 2026-10-13
To check that a receipt really came from AttestPage and was not altered, send it to POST /v1/receipt/verify. It is free: no payment, account or key. The route checks the signature against our published keys and returns valid, the key that signed it, the tier and the decoded payload.
This guide checks a sample receipt with curl and jq against https://vehcdj664efetfrsolne5umanq.srv.us, then shows the same call as an MCP tool. For every field and error, see Verify a receipt. To check without calling our server at all, see Verify a receipt offline.
What the check does
- Checks the signature. The receipt is a compact JWS signed with Ed25519. The route looks up the signing key in our JWKS and checks the signature and the issuer.
- Returns the payload. On success you get the decoded fields, such as
kind,url,content_sha256andretrieved_at. - Names a reason when it fails.
validisfalseandreasonis one ofmalformed,bad_alg,bad_signature,unknown_kid,bad_issortier_key_mismatch. - Says what kind of receipt it is.
tierispaid,trialorsample. Sample receipts use a separate key and say nothing about a third-party page. - Says when the key is retired. A receipt signed by a key we have since retired still checks as
valid, and the answer addsretired: true. The signature is still good; the key is no longer used to sign new receipts. - Stores nothing. The receipt is checked and dropped.
A valid receipt records what our fetcher saw at the time it was signed. It does not say the page was correct.
Price
Free. POST /v1/receipt/verify is not charged, needs no x402 payment and has no trial limit. Paid routes that issue receipts are listed in pricing.json; they are paid in USDC with x402 v2 on Base mainnet (real USDC).
1. Get a sample receipt
The free sample fetch returns a receipt signed with the sample key. Save it as receipt.txt:
curl -s https://vehcdj664efetfrsolne5umanq.srv.us/v1/sample/fetch | jq -r .receipt > receipt.txt
A receipt from a paid or trial call works the same way: take the receipt field of the answer.
2. Check it
jq -Rs '{receipt: rtrimstr("\n")}' receipt.txt \
| curl -s -X POST https://vehcdj664efetfrsolne5umanq.srv.us/v1/receipt/verify -H 'content-type: application/json' -d @- \
| jq '{valid, tier, kind: .payload.kind, url: .payload.url}'
{
"valid": true,
"tier": "sample",
"kind": "fetch",
"url": "https://vehcdj664efetfrsolne5umanq.srv.us/v1/sample/page"
}
The body is {"receipt": "<compact JWS>"}, at most 163,840 characters. A body that is not JSON, or a missing or too long receipt, gets 400.
3. See what an altered receipt looks like
Change the last four characters of the receipt and check it again:
r=$(cat receipt.txt)
jq -n --arg r "${r%????}AAAA" '{receipt: $r}' \
| curl -s -X POST https://vehcdj664efetfrsolne5umanq.srv.us/v1/receipt/verify -H 'content-type: application/json' -d @- \
| jq '{valid, reason, message}'
{
"valid": false,
"reason": "bad_signature",
"message": "Signature does not verify."
}
Treat any receipt with valid: false as unusable. The route still answers 200, so read valid and do not rely on the status code.
4. Read the payload
When valid is true, payload has the facts the receipt records. For a fetch receipt:
jq -Rs '{receipt: rtrimstr("\n")}' receipt.txt \
| curl -s -X POST https://vehcdj664efetfrsolne5umanq.srv.us/v1/receipt/verify -H 'content-type: application/json' -d @- \
| jq '.payload | {kind, iss, retrieved_at, content_kind, http_status}'
iss is the issuer, https://still-rapids-9yt7.here.now. A receipt whose issuer differs fails with bad_iss. To match page text to the receipt, hash the text with SHA-256 and compare it with content_sha256.
With MCP
The same check is the free MCP tool attestpage_verify_receipt, with {"receipt": "<compact JWS>"} as its arguments. To use it from an MCP client with nothing to install, add the remote endpoint:
{
"mcpServers": {
"attestpage": { "type": "http", "url": "https://vehcdj664efetfrsolne5umanq.srv.us/mcp" }
}
}
One call to the remote endpoint with curl, using the receipt from step 1:
jq -Rs '{jsonrpc: "2.0", id: 1, method: "tools/call", params: {name: "attestpage_verify_receipt", arguments: {receipt: rtrimstr("\n")}}}' receipt.txt \
| curl -s -X POST https://vehcdj664efetfrsolne5umanq.srv.us/mcp -H 'content-type: application/json' -d @- \
| jq '.result.content[0].text | fromjson | {valid, tier}'
{
"valid": true,
"tier": "sample"
}
Questions
How much does it cost to check a receipt?
Nothing. POST /v1/receipt/verify and the MCP tool attestpage_verify_receipt are free, with no payment and no trial limit.
Does a valid receipt mean the page is accurate?
No. It means we signed it and it has not changed since. It records what our fetcher saw at the time, not whether the page was right.
Can I check a receipt without calling your server?
Yes. The signing keys are public in the JWKS, so any Ed25519 and JWS library can check a receipt. Verify a receipt offline shows how.