# 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

```json
{
  "id": "rcp_6bd223c5…",
  "transactionId": "txn_498b5328…",
  "providerId": "prv_3652ec9e…",
  "consumerId": "wal_f33b5618…",
  "capabilityId": "cap_d2383f37…",
  "amount": { "amountMinor": "100", "currency": "USD" },
  "responseHash": "9f2c…",
  "status": "success",
  "timestamp": "2026-09-24T10:12:03.114Z",
  "signature": "…",
  "signingKeyId": "rk_…"
}
```

| Field | Meaning |
|---|---|
| `status` | `success`: charged and delivered. `failed`: the seller's handler failed and the buyer was refunded. `refunded`: refunded after the fact |
| `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.

## Two checks, two claims

Both SDKs 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 and verify:

```bash
curl -s https://api.zanora.dev/v1/platform/receipt-keys/$SIGNING_KEY_ID
# → { "keyId": "rk_…", "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](https://docs.zanora.dev/api/receipts.md).
