Core concepts
Receipts
Every paid call produces a receipt signed by Zanora and bound to the exact response. What it contains, how to check it, and how to find old ones.
What a receipt says
{
"id": "rcp_6bd223c5…",
"transactionId": "txn_498b5328…",
"providerId": "prv_3652ec9e…",
"consumerId": "wal_f33b5618…",
"capabilityId": "cap_d2383f37…",
"amount": { "amountMicros": "1000000", "currency": "USD" },
"responseHash": "9f2c…",
"status": "success",
"timestamp": "2026-09-24T10:12:03.114Z",
"signature": "…",
"signingKeyId": "key_…"
}| Field | Meaning |
|---|---|
status | success: charged and delivered. failed: the seller's handler failed and the buyer was refunded. refunded: Zanora reversed a successful purchase after the fact (see below) |
responseHash | sha256 of the response the seller returned. For MCP, it covers the tool's structuredContent when there is one, and its content blocks otherwise |
consumerId | the agent wallet that paid |
signature, signingKeyId | ed25519 over the canonical JSON of every other field |
Receipts contain opaque ids only: no names, emails or request bodies.
Reversed purchases
If a call succeeded but you're unhappy with the result, contact the seller first. You can also email support@zanora.dev within 30 days of the purchase with the receipt id (Terms §5). If Zanora reverses it:
- the buyer's wallet gets the full price back, the seller's wallet gives up what it was paid for the call, and Zanora gives up its fee. All three are new ledger entries; nothing is edited;
- a second, signed receipt is issued for the same
transactionIdwithstatus: "refunded". The originalsuccessreceipt stays as it was, because it is still a true record of what was bought and delivered; - a purchase is reversed at most once, and not after 30 days.
A seller who has already been paid out for the call can end up with a negative balance, which is settled from future earnings.
Two checks, two claims
The SDK and both MCP packages return two booleans with every purchase. They're separate on purpose:
| Check | Claim |
|---|---|
receiptVerified | Zanora signed this receipt, and it hasn't been changed |
responseHashVerified | this receipt is for the answer you received, not some other answer |
A verified receipt with responseHashVerified: false means what arrived isn't what the seller was paid for. The answer may still be useful, but you can't cite the receipt as proof of it.
Verifying a receipt yourself
Receipts are signed over canonical JSON: keys sorted, amounts as strings, without signature and signingKeyId. Fetch the public key for the key id (no API key needed) and verify:
curl -s https://api.zanora.dev/v1/platform/receipt-keys/$SIGNING_KEY_ID
# → { "keyId": "key_…", "publicKeyPem": "-----BEGIN PUBLIC KEY-----…" }Old keys stay available after rotation, so an old receipt can always be checked. In code, agent.verifyReceipt(receipt) in @zanora/sdk, or the zanora_receipt MCP tool, does this for you.
Finding receipts
| You want | Call |
|---|---|
| everything your workspace bought | GET /v1/receipts with a workspace key |
| everything you sold | GET /v1/receipts with a provider key |
| one receipt | GET /v1/receipts/:id |
| all receipts for one transaction | GET /v1/transactions/:id/receipts |
| from an MCP client | zanora_purchases (buyer) or zanora_sales (seller) |
Lists are newest first. To page through, pass ?after=<last receipt id> and follow nextCursor. The cursor is tied to a row rather than an offset, so new sales arriving between pages can't make you skip any. Details are in the API reference.