Receipts and viewing keys

A receipt is a link that opens one transfer. Its owner makes it, and anyone holding it can verify that transfer in a browser.

The transaction viewing key

Every transfer is sealed under its own transaction viewing key, drawn by the sender for that transfer alone. The key opens that transfer and nothing else: no other transfer, no balance, no history.

account.receipt(id) exports the key as a link. Making a receipt changes nothing on chain.

Made byLinkOpens
The sender/receipt/<signature>#sender.<key>Every output of the transfer.
A recipient/receipt/<signature>#recipient.<slot>.<key>The one output paid to them.

The key stays in the browser

The key is the fragment of the link, the part after #. A browser keeps the fragment to itself: it is not part of the request, so no server receives it, Offrecord included.

The receipt view asks the API for one thing, the transfer’s public chain data by signature. Opening and checking happen locally.

What a reader sees

The reader opens the link in any browser, with no wallet and no account. The view then:

  1. Fetches the transfer’s public chain data by its signature.
  2. Opens the sealed outputs with the key from the fragment.
  3. Rebuilds each output’s commitment from what it opened.
  4. Compares it with the commitment published on chain.

When they match, the transfer shows as verified with its amount, asset, sender, recipient and time.

The transfer in the Ring

Sender
Recipient
Asset
Amount
Commitment
9c41e0…b7d2

Opened with the receipt

Sender
7Fq2…mK9d
Recipient
Bx4T…e1Zr
Asset
USDC
Amount
250.00
Commitment
9c41e0…b7d2
The same transfer, in the Ring and opened with its receipt. Sample values.

A key that does not open the transfer gives “This receipt does not open this transfer”. A signature the Ring has no transfer for gives “No such transfer”.

Receipts in code

receipt.ts
// The owner: a link for one transfer.
const link = await account.receipt(transfer.id)

// The reader: no wallet, no account.
const receipt = await offrecord.verifyReceipt(link)

receipt.amount // base units, as a decimal string
receipt.asset.symbol
receipt.sender
receipt.recipient
receipt.time
receipt.commitment // the published commitment the rebuilt one matched

parseReceiptLink and formatReceiptLink read and write the link without touching the network. Both are in the SDK reference.

The account viewing key

For someone who should see everything, an accountant for instance, the owner exports the account viewing key as a file. Its holder scans the Ring and reads every transfer of that account. The key reads. It does not spend.

TypeScript
const contents = await account.exportViewingKey()

In the app, the export is on the view of any transfer in activity, beside Create receipt.