Use CasesDollar Accounts

Dollar accounts in your app

Give customers a balance they fund by bank transfer, hold as stablecoins, and spend from.

Your customers hold a balance and spend from it. Deposits arrive as fiat and settle into a wallet you show them.

The shape

Two things fund the balance: a bank deposit that converts, and a plain on-chain send from anyone. Your product has to handle both, because the second one happens without any call from you.

Build order

StepCall
1. OnboardPOST /api/customers with endorsements: ["endo_usd"]
2. SubmitPOST /api/customers/{customer_id}/verifications
3. Create the walletPOST /api/customers/{customer_id}/wallets
4. Create the funding accountPOST /api/customers/{customer_id}/virtual_accounts with {"kind": "wallet", "asset": "usdc", "wallet_id": "wal_..."}
5. Show the balanceGET /api/wallets/{wallet_id}
6. Show activityGET /api/wallets/{wallet_id}/history
7. SpendPOST /api/wallets/{wallet_id}/operations, then authorize

The balance is a chain read

GET /api/wallets/{wallet_id} returns balances read from the chain at request time, not from a ledger we keep. For your product, that means:

  • Direct deposits are spendable as soon as they land. Someone can pay your customer by pasting the address into any wallet app.
  • insufficient_balance is judged against the chain when an operation is created, less what other operations already commit (executing, or awaiting authorization and not yet expired), not against a number you cached.

Balances ride single-GET responses only. GET /api/wallets returns the same objects without a balances key, so list pages stay cheap. Use GET /api/wallets/total_balances for a portfolio roll-up.

GET /api/wallets/{wallet_id}/history is an activity log, not the source of truth for balances. Render the balance from the wallet object, and use history for the transaction list beneath it.

Deposit addresses are permanent

Each wallet is one address on one chain, and the address never changes. Show it as a QR code and let customers reuse it.

What to watch

  • wallet_transaction.created with kind: "deposit" is your most important event. It fires for money that arrived without any API call of yours, and it is the only notification you get that a customer was paid.
  • Sends need your signing service to be up. If it is down, sends stop. Deposits, balances, and history are unaffected. Decide what your UI shows in that window before it happens.
  • Every send is metered, and gas passes through at cost plus your plan's markup. See Fees & Quotes.

The design decision to make early

A dollar-account product is the case where the 30-second authorization window bites hardest, because a human is usually pressing the button.

Put the human decision before you create the operation. Your UI confirms the send, and only then does your backend create, verify, sign, and authorize in one synchronous block. Creating the operation while a confirmation dialog is open will expire it. See An operation expired before signing.