The model
A collectable is a one-satoshi ordinal output. The wallet holds it in storage basket1sat, and your app addresses it three different ways depending on what you are doing:
Your app builds the inscription script. The wallet funds the transaction, signs it, files the output, and shows the user what they are approving. Item calls always prompt — auto-pay never covers them.
Prerequisites: a connected origin and, for reads, an item view grant.
Read inventory
Reads use scoped permission baskets, never the storage basket. See Permissions and scopes for the full grammar and grant behaviour.All items
totalOutputs is what your grant exposes, not the wallet’s true holdings. A user who approved two collections sees two collections here, with HTTP 200 and no error.
One collection
One exact row, with its transaction
Item vocabulary
Tags
An item with none of
name, app, collection, content, or id is invisible to third-party apps, so always tag at least a name.
customInstructions
Send a JSON string. The wallet reads these fields:
Mint an item
An item mint is acreateAction with no inputs and at least one one-satoshi output in an item basket:
name: tag. Refusal returns 403 ACTION_DENIED; funding problems return 400 INSUFFICIENT_FUNDS or INSUFFICIENT_OR_STALE_FUNDS.
Transfer an item
A transfer spends the held tip and creates the next one. Name the held row with a spend label so the wallet knows which item you mean:p 1sat input id <rowId>. Each label must resolve to exactly one held row and that row’s outpoint must appear in inputs, or the call fails with 400 INVALID_P1SAT_SPEND before the user is ever prompted.
Supply inputBEEF for the tip’s source transactions. If you send unlockingScriptLength instead of a script, you get a staged transaction back and must finish that exact reference with signAction — the wallet does not hold your unlock recipe and never exposes a root private key.
Covenant-locked tips cannot be spent with a plain P2PKH unlock; the wallet refuses rather than guessing. If a tip has gone missing on chain, that is a wallet-side abandon in HandCash, not something an app resolves with relinquishOutput.
Burning
There is no burn method for apps. A burn is a transfer whose output is a protocol-valid terminal spend that your app constructs. Never turn a failed item path into a plain payment.Receive an item
protocol must be the string basket insertion, and the remittance basket is the storage basket 1sat.
The wallet validates the transaction before accepting it. Do not tell the sender a delivery succeeded until this call resolves. The user sees a Receive item prompt the first time; after that a stored receive grant can carry it.
Authenticity
HandCash shows an item as verified only when a complete BRC-150 v2 proof checks out. The proof is a local object incustomInstructions.provenance:
Two rules matter for app authors. Provenance is local metadata — it does not ride along on a P2PKH lock, so a recipient’s wallet reconstructs it. And an oversized proof is omitted, never truncated: the wallet’s cap is 400,000 characters of BEEF, above which the item stays unproven rather than carrying a partial lineage.
Release a row
p 1sat all here: scoped forms that require a tag cannot carry one on this call.