Skip to main content
Beta — BRC wallet only. Desktop / Mobile over BRC-100.
Three independent gates decide whether a call succeeds:
  1. Origin — is your app connected at all?
  2. Action approval — did the user approve this specific transaction, signature, or disclosure?
  3. Scoped view — did the user grant a view of the items, tokens, or catalog you asked for?
A connection grants none of the others, and an item view never implies a token view.

How the wallet identifies your app

The wallet resolves your identity per request:
  1. The browser-supplied Origin header, reduced to its host.
  2. Otherwise the originator header, parsed as a URL and reduced to its host.
  3. Otherwise no origin, which is the wallet’s own internal path — not available to apps.
Hosts are lowercased with www. stripped, so https://App.Example.com/x and https://www.app.example.com are the same app. localhost and 127.0.0.1 are different apps. Grants are stored per wallet account, so switching accounts in HandCash starts from a clean slate. Always send originator when you are not in a browser. Never claim another site’s origin.

Method tiers

Connect an origin

isAuthenticated never prompts, so it is safe on page load. A declined connection returns { authenticated: false } with HTTP 200 from waitForAuthentication; any other method on an unconnected origin returns 401 NOT_AUTHENTICATED, and a declined origin returns 403 PERMISSION_DENIED. Grants persist across restarts. The user can disconnect your app in HandCash settings, which also clears its auto-pay and spending limit. While the wallet is locked, everything except GET /health and GET /manifest.json returns 503 WALLET_LOCKED.

Action approval

These always prompt, every time, and are never covered by Pay or auto-pay:
  • Item mints, item sends, item releases
  • Token mints, token sends, token releases
  • createSignature, encrypt, decrypt, key-linkage reveals, certificate acquisition and disclosure
These can proceed without a prompt once the user has opted in: Concurrent duplicate requests from one origin may share a single prompt, except for createSignature, createAdminIdentityProof, and market mutations, which always get their own.

Auto-pay and spending limits

Auto-pay is the user’s setting, not an API. When they enable it, the default ceiling is 10 USD per 24 hours, and each payment must also fit under that per-payment cap. Spending is counted from your origin’s payment activity, and item activity never counts against it. Your manifest can propose a monthly cap instead, which the wallet reads from metanet.groupPermissions.spendingAuthorization (Babbage’s babbage.groupPermissions is also accepted):
amount is satoshis per UTC calendar month. The wallet fetches manifest.json from your origin while the connect prompt is already open and gives up after 1.5 seconds, so keep it fast and cacheable. A granted monthly cap replaces the USD window, but it never enables silent payments on its own — the user must still turn auto-pay on.

Scoped inventory views

Reads of items, tokens, and catalog packs go through permission baskets, not storage baskets. The form is p <scheme> <scope>, and filter values go in tags rather than in the basket name. Scope tokens are lowercase and exact. Storage baskets (1sat, bsv21) are wallet-internal and refused here. Where each form belongs:

What a grant actually returns

  • A grant is a ceiling, and the wallet filters results down to it. A narrower grant returns fewer outputs with HTTP 200, not an error.
  • Only a refusal at the prompt produces 403 ITEM_VIEW_DENIED or 403 TOKEN_VIEW_DENIED.
  • Third-party apps never hold a persistent “all” grant. Asking for p 1sat all turns into a filtered grant over the collections and apps the user approved, so plan for a subset and paginate.
  • p 1sat id with a specific row id is a narrow lookup and resolves without a prompt.
  • Item view and token view are separate grants. Holding one and calling the other prompts again.
  • Rows with no identifying metadata are never returned to third-party apps.

Catalog packs

Catalog packs (index expansions) let your app read a shared index offline and query a live overlay through it: Reading a pack through listOutputs uses p index read <packId>; using p index install or p index sync there returns 400 INVALID_INDEX_BASKET.

Errors

Deadlines

The bridge holds a request while the user decides: about 120 seconds for reads and prompts, and about 300 seconds for spends. Past that you get 503 WALLET_BRIDGE_TIMEOUT for a read or 503 WALLET_BRIDGE_PENDING for a spend. WALLET_BRIDGE_PENDING does not mean failure — the wallet may still complete the action. Reconcile with listActions by label, or by protocol state, before you retry anything that mutates. If your HTTP client disconnects, the wallet cancels the pending prompt.

Designing for the prompt

The prompt is the user’s whole view of your request, so:
  • Write a specific description; the wallet shows it verbatim.
  • Give every input and output a real inputDescription / outputDescription.
  • Expect the wallet window to take focus, and do not block your UI on a modal of your own.
  • Item and token previews come from wallet-held data. A preview you supply is not displayed, so it cannot be used to misrepresent an asset.

Reserved for HandCash hosts

Market, migration, handle-claim, and operations methods check the calling host and refuse everyone else: Build on createAction, signAction, and internalizeAction instead. Those are portable to any BRC-100 wallet.

Next steps