The screens, statuses, and error messages behind a neobank's Add money and Withdraw buttons, built on on-ramp and off-ramp APIs, with a provider check.
A neobank's Add money and Withdraw flows are on-ramp and off-ramp calls dressed as banking screens. With BlindPay, Add money is a virtual account or a payin quote, and Withdraw is a bank account, a locked payout quote, and a payout. Design a screen for every status the API returns, and confirm only on locked numbers.
An on-ramp turns bank money into a stablecoin, a token that tracks a currency like the dollar. An off-ramp turns it back into money in a bank account. Your users don't need either word. This guide maps both flows screen by screen, with the states and messages each one needs. The product context lives in how to build a neobank.
Key takeaways
Two patterns. Permanent account details for repeat deposits, and a one-time instruction for local-currency top-ups.
| Pattern | When to use it | What the user sees | BlindPay object |
|---|---|---|---|
| Account details | Salaries, client payments, repeat deposits in USD | Routing and account number in their own name | US virtual account |
| One-time instruction | Top-ups in BRL, MXN, COP, or ARS | A Pix code, a CLABE, a PSE link, or a Transfers 3.0 account | Payin quote, then payin |
| Pull from linked bank | US users with a linked account | "Add $200 from Chase" style button | ACH pull from a Plaid-connected account (enabled on request) |
For account details, the build is in give every customer US account details. For the one-time pattern:
failed.For SPEI, show the CLABE in groups and with a copy button. Banxico's SPEI page tells payers they need the 18-digit CLABE and that banks often make them register a new recipient first. Warn first-time payers about that delay.
Five screens, in this order. Each maps to one BlindPay call or webhook.
expires_at.payout.new fires once the funds are captured.The review screen carries the most risk. How to show a cross-border quote covers the disclosure rules in detail. In short: show the rate, every fee, and the total, and don't paint an estimate as a promise. For US consumer remittances, the Remittance Transfer Rule disclosures require the exchange rate rounded to between two and four decimal places, among other items.
Show it, warn before it ends, and re-quote on expiry. A silent expiry turns into a failed confirm tap.
WCAG's timing guidance asks that users be warned before a time limit ends and given at least 20 seconds to extend it with a simple action. "Refresh quote" is that action. Never extend by executing on stale numbers.
Illustrative example. Numbers are invented. A user withdraws $500 to a Mexican account.
| Moment | Screen shows |
|---|---|
| Quote created | Rate 18.40 MXN per USD, fee $3.00, recipient gets MX$ 9,144.80, 5:00 left |
| 4:40 elapsed | "Your rate expires in 20 seconds. Refresh to keep going." |
| Expired, user taps Refresh | New rate 18.36, recipient gets MX$ 9,124.92. Old amount shown struck through |
| User confirms | Payout created from the new quote |
Showing both amounts after a refresh stops the "you changed my rate" ticket before it starts.
One message per status. Map them once and reuse them in the app, push notifications, and email.
| Flow | BlindPay status | Message | Push? |
|---|---|---|---|
| Add money | processing | "Waiting for your deposit" | No |
| Add money | on_hold | "We're reviewing this deposit" | Yes |
| Add money | completed | "$X added to your balance" | Yes |
| Add money | failed | "We didn't receive this deposit" | Yes |
| Add money | refunded | "This deposit was returned to the sender" | Yes |
| Withdraw | processing | "Sending to your bank" | No |
| Withdraw | on_hold | "Your transfer is under review" | Yes |
| Withdraw | completed | "Delivered to account ending 1234" | Yes |
| Withdraw | failed | "Your transfer didn't go through. Here's what happens to your money" | Yes |
| Withdraw | refunded | "Your money is back in your balance" | Yes |
Failure copy must match how refunds work. On BlindPay, if a payout cannot be delivered after the stablecoins are collected, they are refunded to the quote's refund wallet, or to the sending wallet when none is set. On Pix and other non-USD local rails the refund is automatic. USD payouts, and payouts whose review times out, are refunded after an operator review. Say "usually within" only for the automatic case. Payout statuses explained has the full lifecycle.
Score ten things. Each one shows up on a screen above.
| # | Criterion | Screen it affects |
|---|---|---|
| 1 | Quotes lock rate and fees until a stated expiry | Review |
| 2 | Fee breakdown returned before confirm | Review |
| 3 | Named payout rails per country | Choose bank account |
| 4 | Payin methods per country (account details, Pix, SPEI, pull) | Add money |
| 5 | Clear status model with signed webhooks | Processing, notifications |
| 6 | Documented refund behavior per rail | Failure screens |
| 7 | KYC and KYB in the same API | Onboarding |
| 8 | Published limits and minimums | Amount entry |
| 9 | Custody model you can explain to users | Balance, failure copy |
| 10 | A test environment that can force failed and refunded outcomes | All of them, before launch |
The 10-point liquidity scorecard adds depth tests, and where neobanks hold customer funds covers criterion 9.
BlindPay covers both buttons with one API. Add money runs through US virtual accounts in each customer's own name, or through payin quotes that return a Pix code, a CLABE, a PSE link, or a Transfers 3.0 account, with ACH pull from a Plaid-connected account when enabled. Each deposit becomes a payin that settles to the customer's wallet as USDC or USDT.
Withdraw is a saved bank account, a payout quote that locks the rate and fees for 5 minutes, and a payout over Pix, SPEI, ACH, RTP, wire, ACH Colombia, Transfers 3.0, SEPA, or SWIFT (POBO/COBO). Payouts pull the quoted amount at execution, so there is no balance to pre-fund. Statuses map one to one onto the screens above, and Svix-signed webhooks drive your notifications.
KYC, KYB, and sanctions screening run inside the same flows. On a development instance, sentinel amounts force failed and refunded outcomes so you can see every screen before launch.
List your screens against the status table above and mark any status with no message. Then run one payin and one payout on a development instance, using the payout quotes docs and the payins docs.
Always cheaper? No intermediaries? Recipient needs a wallet? 10 common claims about cross-border stablecoin payments, checked against the Fed and the BIS.
Seven places blockchain payments beat bank rails: contractor payroll, remittances, B2B suppliers, marketplaces, 24/7 treasury, bill pay, and PSP payouts.
The dates that turned stablecoins from a trading tool into cross-border payment rails: USDC, Pix, MiCA, the GENIUS Act, ISO 20022, and Visa settlement.