Skip to main content
The Payment Widget’s deposit flow handles the entire bank deposit setup in one session: the user picks the destination Uphold account, selects Bank, completes any bank setup the network requires and gets the transfer instructions to use in their banking app — all without your backend setting up a deposit method or creating a second session. The Widget picks the network from the bank rails available to the user: FPS (GBP) for UK users, and ACH, FedNow / RTP and Wire (USD) for US users, which share the same transfer instructions.
Bank deposits are push transfers: the user sends the funds from their own bank, so the Widget does not create any transaction. Uphold creates the transaction when the transfer arrives. Monitoring and processing it must be handled by your backend via webhooks or polling.
If you need to build the deposit experience yourself, see the select flow guides for FPS and ACH, FedNow / RTP and Wire instead. To compare all flows, see Flows.

Prerequisites

  • The user has completed onboarding and has the required capabilities enabled.
  • The Payment Widget is set up in your frontend. See Installation and setup.
  • @uphold/enterprise-payment-widget-web-sdk is on version 0.19.0 or later.

Walkthrough


Complete the deposit in the widget

The widget session takes the user from account selection through bank setup to the transfer instructions.

Create a widget session

Call Create widget session to start the deposit flow. No data is required — the widget gathers everything from the user. Use options to customize the Widget’s theme and layout, as well as the flow itself. See the example here and the SDK reference for all available options. By default, the end-to-end flows offer both card and bank. To offer bank only, set paymentMethods to bank:
In the end-to-end flows, use availableAssets to restrict the assets the user can deposit into. It replaces any assets filter set on the bank payment method.
The response contains a session object with a url field that loads the widget. Return response.session to your frontend as is.

Set up the widget

Both examples above are for web applications — either creating the iframe yourself or letting the SDK do it. For native apps using a WebView, see Native apps with the Web SDK for the SDK’s native-bundling pattern, or Setup without the SDK for the no-SDK approach that loads the session url directly as the WebView’s top-level page.
The transfer instructions screen offers a Share action that opens the device’s share sheet, only when the browser supports the Web Share API and the iframe allows web-share. The Web SDK’s mountIframe() grants web-share. Without the SDK, add web-share 'src' to the iframe allow attribute, as in the example above. When sharing isn’t available, the action is hidden and the user can still copy each detail.

Account rules for US deposits

US bank deposits (ACH, FedNow / RTP and Wire) can only be credited to the user’s default USD account. When the user picks another account, the Widget handles it:
  • Other payment methods are available: Bank is shown as available for the default account only, with a Switch account action. It switches to the user’s default USD account, creating it if needed, and shows the transfer instructions with a notice that the funds will go to that account.
  • Bank is the only payment method: the Widget shows that the action is unavailable for the chosen account and lets the user go back and pick another one. If the account came from initialSelection, going back closes the Widget with a cancel event.
If availableAssets excludes USD, the user can’t pick the default USD account, so US bank deposits are not offered. In a bank-only session, the Widget shows that the action is unavailable, and going back closes the Widget with a cancel event.
To skip the account-selection screen for US users, set initialSelection to { "destination": { "asset": "USD" } }, which resolves to the user’s default USD account, or pass that account’s ID as { "destination": { "accountId": "..." } }.

Handle the complete event

Unlike card deposits, the complete event of a bank deposit carries a selection rather than a transaction: it fires when the user closes the transfer instructions screen, with via: "deposit-method" and the deposit method that was shown. Because the deposit flow can complete with either a card transaction or a bank selection, check which one you received: only the bank selection has a via property, and checking it with in also narrows the type in TypeScript.
For bank deposits, the event payload contains:
  • via — set to deposit-method.
  • selection — contains the account that will receive the funds and the depositMethod with the transfer instructions. For FPS, the user can still change the receiving account from the transfer instructions screen, so account may differ from the one picked at the start.
The widget presents these details directly to the user, so you don’t need to display them separately. depositMethod.details.network indicates the network: "fps" for UK users, and "ach" or "fednow" (FedNow and RTP) for US users, with secondaryNetworks listing the other US networks that share the same instructions. For US deposits, the network the transfer actually arrives on depends on how the user sends it from their bank — for example, a high-priority transfer arrives via FedNow / RTP — and is set in the transaction’s origin.node.network.
complete only means the user saw the transfer instructions, not that they sent the transfer. A user can also close the Widget and send the transfer later using the same instructions. Treat webhooks as the source of truth and complete as a UX signal.

Handle cancellations

The cancel event fires when the user closes the widget before reaching the transfer instructions.

Handle errors

The Payment Widget handles most errors internally. For unrecoverable errors, the widget fires an error event. It is the host application’s responsibility to handle these events, present an error message to the user, and unmount the widget.
The widget handles bank setup issues in-flow — for example, when the bank details can’t be set up, Bank is shown as unavailable so the user can pick another payment method. The error event is reserved for failures the widget cannot recover from.

Monitor for the incoming transfer

The widget presents the deposit instructions to the user but does not monitor for the incoming transfer. Your application must do this via webhooks or polling. In sandbox, use the Simulate bank deposit test helper with the depositMethod details from the complete event to simulate the incoming transfer.

Sample transaction

In a successful FPS deposit, the origin is represented as an external-account node. This is because the origin bank details are automatically registered as an external account. The destination is the account selected when the deposit instructions were generated, or the user’s default GBP account if the transfer was sent without a reference, or the selected account doesn’t exist anymore.
The new external account can be then retrieved with Get external account to show the sender’s bank details alongside the transaction, or to withdraw funds to the same account in the future.

Notify the user

Display an in-app confirmation when the transaction is completed, and send an email if applicable.
You now support end-to-end bank deposits via the Payment Widget.