> ## Documentation Index
> Fetch the complete documentation index at: https://docs.joinbankroll.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> Release history for @joinbankroll/sdk. The reference pages describe the latest version.

The reference pages track the **latest** `@joinbankroll/sdk`. This page is the per-version history — check it to see when a feature landed or what changed between the version you have and the latest. Install the latest with:

```bash theme={null}
npm install @joinbankroll/sdk@latest
```

Versions follow [semantic versioning](https://semver.org). While the SDK is pre-1.0 a minor release can carry a breaking change; those are called out below.

<Update label="0.16.0" description="August 19, 2026">
  **`haptics()` — the physical half of your UI.** [`bankroll.haptics({ type })`](/build/haptics)
  plays a typed vibration through the phone's engine: impacts (`light` / `medium` /
  `heavy`), outcomes (`success` / `warning` / `error`), or `selection`. An omitted
  or unknown type plays the host default, a heavy impact.

  Decoration only, so unlike every other capability it **never rejects** — in a
  plain browser, under an old host, or on any bridge failure it resolves having
  done nothing. Needs a Bankroll host at client version 4+, feature-detected; no
  new manifest claim, and no re-consent for already-connected apps.

  **Offers on your tile.** Serve [`/.well-known/bankroll-status`](/build/status)
  and Bankroll rings your tile with your headline when you have something on
  offer for the person asking — per user when they've connected you, your
  generic new-user offer when they haven't. Opening from the offer hands its
  `key` back to your app as `?offer=`. Decorative only; nothing to install.

  **Bankroll Verified.** The green ✓ after an app's name means Bankroll reviewed
  what the origin serves and signed its manifest. [What it means, what it
  unlocks, how to get it](/build/verified) — and it is now what makes play in
  your app count toward a friend's referral.

  **Referral links, in numbers.** [Share links](/build/share-links) now state the
  program your `playLink({ referrer })` feeds: new user attributed at signup, $30
    of play across verified apps within 30 days, $10 each to both sides — and \$10
  to a verified app's treasury per qualifying referral from its links.
</Update>

<Update label="0.15.0" description="August 18, 2026">
  **Know a payout's signature before you broadcast it.**
  [`buildAndSignPayout()`](/build/payouts#the-payout-lifecycle) builds and signs
  in one call, returning the transaction, its deterministic signature, and its
  expiry with nothing sent — so your payout row stores the signature in the same
  write that locks it, and recovery is always a question the chain can answer.
  `signPayout()` does the signing half for bytes you built separately, and a
  custom `PaymentSigner` can opt in with `signTransaction`.

  Purely additive — no existing call changes behavior. The payout lifecycle docs
  now teach the full order: build → sign → store → send → confirm.
</Update>

<Update label="0.14.1" description="August 18, 2026">
  **`iconDigest` on `manifestRoute`.** [`manifestRoute()`](/build/next#the-manifest-route)
  takes an optional `iconDigest` — a Subresource Integrity hash of your icon's
  bytes. A changed digest is a manifest change, which is what carries a
  [replaced icon](/build/manifest#updating-your-icon--icondigest) to users who
  already connected. Omitted, like `supportUrl`, whenever it resolves to nothing.
</Update>

<Update label="0.14.0" description="August 18, 2026">
  **Find a charge your page never reported.** [`createReference()`](/build/payments#recovering-a-lost-charge)
  mints an id your server stores with the order and passes to `charge()`. The
  payment carries it on-chain, so when the page dies between the charge settling
  and the request that would have reported it,
  [`findChargeByReference()`](/build/payments#recovering-a-lost-charge) still
  finds the charge — by an id that existed before it did. `ConfirmedCharge` now
  carries the payment's `signature`, so a recovered charge settles through the
  same code as a reported one.

  **[`expiresInSeconds`](/build/payments#expiring-a-stale-price) bounds a stale
  price.** A charge left sitting on the pay sheet rejects `charge_expired` rather
  than settling against a price that has moved — nothing signed, nothing moved.
  It defaults to 90 seconds, and the host counts it down on the sheet.

  Passing a reference needs a current Bankroll app — older ones reject with
  `update_required`.
</Update>

<Update label="0.13.0" description="August 13, 2026">
  **`sendPush()` — notify your users through Bankroll.** From your server, sign a
  short-lived request with your push key and Bankroll delivers a notification to
  one of your users, titled with your app's name; tapping it opens your app at
  the path you choose. `pushAddress()` derives the public key your manifest
  declares — [`manifestRoute`](/build/next)'s new `push` entry.

  Push is permissioned: it works once Bankroll has signed your manifest, which
  attests the key, and only toward users who have opened your app. The push key
  is its own secret (`BANKROLL_PUSH_KEY`) — pushing and paying are different
  powers, so it is deliberately not the treasury key.
</Update>

<Update label="0.12.0" description="August 13, 2026">
  **Share links carry a referrer.** [`playLink(url, { referrer })`](/build/share-links)
  appends the wallet of the Bankroll user sharing the link, so an app can
  piggyback on Bankroll's referral program instead of building its own — pass
  `session.user.wallet` and a new Bankroll user who opens the link is attributed
  to them.

  The referrer must be a real Bankroll user's wallet; the program is person to
  person, so an app's own treasury is attributed to nobody. An unrecognized
  referrer costs the attribution, never the link.
</Update>

<Update label="0.11.0" description="August 7, 2026">
  **`balances()` and `deposit()` — prerelease.** [`bankroll.balances()`](/build/balances)
  returns your app's view of the user's value — their cash, your app's credits,
  and the balances of every mint your manifest declares. [`bankroll.deposit()`](/build/balances#deposit)
  presents the host's deposit UI, optionally preselecting a funding source.

  Both are feature-detected and need a current Bankroll host; an older one rejects
  with `update_required`. Prerelease — the contract may still change.
</Update>

<Update label="0.10.0" description="July 31, 2026">
  **`supportUrl` on `manifestRoute`.** [`manifestRoute()`](/build/next#the-manifest-route)
  takes an optional `supportUrl` and omits the claim when it resolves to nothing —
  an empty claim is still a claim, and would re-ask every connected user for
  consent once it gained a value.
</Update>

<Update label="0.9.0" description="July 29, 2026">
  **Breaking: `publicOrigin()` is gone.** It read `BANKROLL_DEV_TUNNEL_ORIGIN` so a
  development landing page could render a link a phone could reach; in production
  it was [`getOrigin()`](/build/next#the-origin) with extra steps. Call `getOrigin()`.

  Removing it is what lets the development CLI start your server first and read
  `launch` from your live manifest, so the QR code it prints opens your app rather
  than your landing page.
</Update>

<Update label="0.8.0" description="July 28, 2026">
  **Breaking: `manifestRoute`'s `appTokens` is a map.** It took a single mint; it
  now takes `Record<mint, { name?, description? }>`, matching the
  [manifest claim](/build/manifest#claims) and letting an app issue several tokens
  that each carry their own display strings.

  ```ts theme={null}
  // before
  appTokens: () => CHIPS_MINT,
  // after
  appTokens: () => ({ [CHIPS_MINT]: { name: "Acme Chips" } }),
  ```
</Update>

<Update label="0.7.0" description="July 28, 2026">
  **Three new entry points**, all optional — the pieces every Built-for-Bankroll
  app was writing itself.

  * [`@joinbankroll/sdk/next`](/build/next) — `getOrigin()`, `getSession()` /
    `requireSession()`, `requireIdentity()`, and `manifestRoute()`, which serves
    `/.well-known/bankroll.jwt` so you never hand-assemble the format. Plus the
    treasury: `treasuryAddress()` derives your payment address from
    `BANKROLL_TREASURY_KEY`, so what your manifest advertises can't drift from the
    wallet that signs.
  * [`@joinbankroll/sdk/store`](/build/store) — durable JSON with an atomic create
    and compare-and-swap, so a payment signature can't be redeemed twice without
    provisioning a database. Filesystem and Vercel Blob backends behind one
    interface; `@vercel/blob` (`>= 2.3.0`) is an optional peer.
  * [`@joinbankroll/sdk/react`](/build/react) — `useBankrollStatus()` /
    `useBankrollChecked()` that agree across hydration, `bankrollFetch`,
    `verifyIdentity()`, and a development overlay. `react` (`>= 18`) is an optional
    peer.

  **`SOLANA_RPC_URL` is now optional.** Unset, the server half falls back to
  Solana's public endpoint and warns once per process instead of failing, so an app
  can take its first payment before configuring an RPC. `usingPublicRpc()` reports
  whether the fallback is in play.

  <Warning>
    Set your own endpoint before you [pay anyone](/build/payouts). The public one
    rate-limits under concurrency, and a 429 while broadcasting a payout surfaces as
    `PayError('rpc_error')` with an unknown outcome — the one failure you cannot
    safely retry.
  </Warning>
</Update>

<Update label="0.6.0" description="July 25, 2026">
  **App tokens.** [`charge()`](/build/payments) and [`pay()`](/build/payouts) take an optional `token` — a mint you issue yourself, declared in your manifest's [`appTokens`](/build/app-tokens) — so you can sell and pay out in your own promo credit with the same calls you use for real money. Declaring your mints is also a limit: your app can charge HSUSD or those mints, and nothing else.

  <Warning>
    **Breaking: `confirmCharge()` no longer guarantees HSUSD.** It now reports the `mint` that paid and leaves the judgement to you, alongside the payee, amount and payer checks you already make. A server that upgrades **without adding a mint check will accept a token the sender minted for nothing** — the signature comes from the client, so any settled transfer reaches `confirmCharge`, and a worthless token passes every other check. Add:

    ```ts theme={null}
    import { confirmCharge, HSUSD_MINT } from "@joinbankroll/sdk/server";

    if (payment.mint !== HSUSD_MINT) throw new Error("paid in another asset");
    ```
  </Warning>

  Every asset a charge settles in carries HSUSD's scale — 9 decimals, one token to the dollar — so amounts stay in whole US cents throughout. Bankroll refuses a mint of any other shape before it signs.
</Update>

<Update label="Manifest" description="July 24, 2026">
  **`iconDigest` manifest claim.** Added the OPTIONAL [`iconDigest`](/build/manifest) claim — a Subresource Integrity hash (`sha256-<base64>`) of your icon's bytes. Declare it so a replaced icon reaches users who have already connected your app, instead of staying cached.
</Update>

<Update label="0.5.0" description="July 20, 2026">
  **`ConfirmedCharge.slot`.** [`confirmCharge()`](/build/payments) now returns the `slot` the transfer landed in — a chain-assigned, monotonic number. Use it as a stable ordering key for a purchase listing without giving up signature-keyed idempotency.
</Update>

<Update label="0.4.2" description="July 20, 2026">
  **More reliable payout reconciliation.** [`confirmPayout()`](/build/payouts) now searches the transaction ledger instead of only the recent status cache, so confirming a long-since-landed payout resolves correctly.

  <Note>
    When you reconcile **old** payouts, confirm against an endpoint with full transaction history — a pruned endpoint can report a landed transaction as `expired`, and paying again on that signal would double-pay.
  </Note>
</Update>

<Update label="0.4.1" description="July 19, 2026">
  **Dependency hardening.** Inlined the token-transfer instructions the SDK builds, removing a transitive dependency that carried an unfixable security advisory. No API changes.
</Update>

<Update label="0.4.0" description="July 18, 2026">
  **Payout lifecycle.** The single-call payout became a three-step lifecycle — [`buildPayout` → `sendPayout` → `confirmPayout`](/build/payouts) — with a caller-owned payout-row state machine (created → submitted → confirmed | failed | expired) and `confirmPayout()` as the reconciliation primitive. `pay()` remains as their composition.
</Update>

<Update label="0.3.0" description="July 18, 2026">
  **Server-side charge confirmation, and paying a user.**

  * Added [`confirmCharge(signature)`](/build/payments) — read a settled charge's facts from chain (`{ payer, payee, amountCents, memo }`) and verify them before releasing value.
  * Added a server payout: `pay({ to, amountCents, memo? }, { signer })`, signed with a `PaymentSigner` — `keypairSigner(secretKey)` or `privySigner` from `@joinbankroll/sdk/privy`.

  <Warning>
    **Breaking — the client charge method was renamed** `bankroll.pay()` → [`bankroll.charge()`](/build/payments). The freed `pay()` name is now the server payout function. Update your charge calls when you upgrade. (The host bridge's wire method stays `pay` — it's a versioned protocol, so nothing on the host side changes.)
  </Warning>
</Update>

<Update label="0.2.1" description="July 16, 2026">
  **Charge error codes.** Added `idempotency_conflict` (an `idempotencyKey` reused with different parameters) and `payment_denied` (the host declined at the pay sheet), and documented `idempotencyKey` on the charge input.
</Update>

<Update label="0.2.0" description="July 14, 2026">
  **`session()`.** The identity capability is now `session()`; `identity()` remains as a deprecated alias for older hosts. Added the `BankrollSession` type on the server entry.
</Update>

<Update label="0.1.0" description="July 9, 2026">
  **Initial release.** The Build-on-Bankroll client and server-side session-token verification (`verifyToken`).
</Update>
