Skip to main content
Bankroll delivers webhooks to one route on your origin, POST /api/bankroll/webhook. It carries one family of events: what became of the managed references your server minted, pay-ins and payouts alike. Deliveries are signed and retried until your route answers 2xx. Bankroll never judges a transaction: it reports the signature, and your server reads the transaction.

Setup

Your exact origin must be Bankroll Verified. Verification creates your endpoint and hands you its secret with the signed manifest; set it as BANKROLL_WEBHOOK_SECRET (builder apps have it already).
referenceWebhook returns a plain (request: Request) => Promise<Response> handler: a Next.js route handler as it stands, and usable anywhere else that takes one. Pass secret to verify with something other than the environment variable.

Events

Bankroll delivers one event per reference. meta is what you gave createManagedReference, verbatim: it is how you find the entry again. A failed transaction moves nothing and is not an event; the reference stays open until something lands or the window ends. Types are exported from @joinbankroll/sdk/webhooks:

What the route does

  • Reads the raw body and verifies the signature against the secret. A delivery that does not verify, or carries no signature, is refused with 401 before any handler runs; a body over 64 KiB with 413; an event the route cannot read with 400.
  • Hands a reference.confirmed to onConfirmed and a reference.expired to onExpired, then answers 200.
  • Lets a handler’s throw propagate. The route answers 500 and Bankroll delivers again later, so never swallow a failure to record what you learned.
Handle each event idempotently: Bankroll retries until it gets a 2xx, so one reference can be told about twice across a retry.

Handling a confirmed reference

A reference is public once it lands, so anyone can attach it to a transaction of their own. reference.confirmed is a candidate, not a receipt: always read the transaction before releasing anything. For a pay-in, checkCharge reads the charge and checks it against what the entry was sold for. For a payout your server built and sent itself, the signature the send answered is the proof: mark the payout paid when Bankroll reports that one, and take Bankroll’s word only when the send’s answer was lost.
checkCharge throws ChargeMismatchError with the field that differed; treat a mismatch as not paid. On reference.expired, release whatever waited on a pay-in, or treat the payout as never sent. The window is yours to size when you mint the reference: nothing lands after a charge’s window plus its blockhash lifetime, and a Privy-signed payout must not be rebuilt inside its 24 hour replay window.

Under the mock

With BANKROLL_MOCK=1 outside production nothing reaches Bankroll. The reference is minted locally, the mock host’s charge() and a mock payout deliver reference.confirmed to your route themselves, unsigned, and the window’s end delivers reference.expired. The route accepts an unsigned delivery only under the mock. npm run check runs the whole loop.