# Receipt demo

Last updated: 2026-10-13

A signed receipt from start to finish, using only free calls: get a receipt from a free example call, check it with `POST /v1/receipt/verify`, see a changed receipt fail, then check it offline. Step 5 adds the free trial on a real route. No payment, wallet or account is needed. The commands use `curl` and `jq`.

## 1. Get a receipt

`GET /v1/sample/fetch` runs the real [fetch](/docs/fetch) code on the [example page](https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/v1/sample/page) and signs the result with the example key.

```sh
curl -s https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/v1/sample/fetch > sample.json
jq '{tier, page: .page.http_status, kind: .page.content_kind, sha: .page.content_sha256}' sample.json
jq -r .receipt sample.json > receipt.txt
```

The response has the page result and a `receipt` field: a compact JWS (`header.payload.signature`). The receipt records the URL, final URL, status, content_kind, page hashes, time of the fetch and a hash of the request.

## 2. Check it

```sh
jq -n --rawfile r receipt.txt '{receipt: ($r | rtrimstr("\n"))}' > body.json
curl -s -X POST https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/v1/receipt/verify -H 'Content-Type: application/json' -d @body.json
```

The answer:

```json
{
  "valid": true,
  "kid": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/.well-known/jwks.json#sample",
  "tier": "sample",
  "payload": {
    "v": 1,
    "iss": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us",
    "kind": "fetch",
    "tier": "sample",
    "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/v1/sample/page",
    "http_status": 200,
    "content_kind": "real",
    "content_sha256": "0e429607…",
    "payment": null,
    "…": "…"
  }
}
```

`payload.content_sha256` matches `page.content_sha256` from step 1.

## 3. Change one character

```sh
jq -n --rawfile r receipt.txt '{receipt: ($r | rtrimstr("\n") | .[0:-2] + (if .[-2:-1] == "A" then "B" else "A" end) + .[-1:])}' > bad.json
curl -s -X POST https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/v1/receipt/verify -H 'Content-Type: application/json' -d @bad.json
```

The answer has `"valid": false` and `"reason": "bad_signature"`.

## 4. Check it offline

The [offline checker](/docs/verify-offline) is one Node.js file. It checks the receipt against the published keys at [/.well-known/jwks.json](https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/.well-known/jwks.json) without calling the verify route.

```sh
curl -sO https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/dl/verify-receipt.mjs
node verify-receipt.mjs "$(cat receipt.txt)" --issuer https://s2h6qt73jtzlgbgnnfacln2poq.srv.us
```

## 5. Try a real route

For a receipt about a URL of your choice, call a paid route such as [fetch](/docs/fetch). There is no free trial at the moment; use the free samples instead.

A free example receipt is signed with the example key and says nothing about any page other than the example page. Paid and trial receipts cover the URL you asked for. See [free example calls](/docs/samples), [Verify a receipt](/docs/receipt-verify) and [Payments](/docs/payments).
