Skip to main content
This flow covers generating a virtual account, retrieving the account details, monitoring webhooks, and crediting the user’s account.

Prerequisites

  • The user is onboarded and verified (KYC is complete).
  • A receiving account to credit the funds.
  • The bank-deposits capability is enabled for the user.
  • The bank transfer network you wish to use is enabled in your environment (e.g. FPS, ACH).
  • Your system is set up to listen to webhooks for incoming transactions.
Bank deposits are push transactions — the user initiates the transfer from their banking app using the account details provided as destination. No quote is required to initiate the deposit because the amount can only be settled when the transfer is received by Uphold.

The flow

The flow can be implemented entirely via the REST API, by directly calling the necessary endpoints, or by using the Payment Widget, which will handle the process for you. Both implementations are described below.

Using the REST API

Follow these steps to perform a bank deposit using the REST API.

Choose the target account

Let the user select the target account to credit. To list the user’s accounts, use the List Accounts endpoint. When the target account asset differs from the bank transfer currency, the conversion will happen when the deposit is settled, using Uphold’s prevailing rate.
  • For US bank networks (ACH, Wire, FedNow) only USD target accounts are supported. I.e., the user can only deposit into a USD account.
  • For UK bank networks (FPS), any currency is supported for the target account. I.e., the user can deposit directly into a BTC account.
    If the user needs a guaranteed rate before sending funds, guide them to a quote-based deposit (e.g., card) instead of a push bank transfer. Alternatively, guide the user into selecting a target account with the same currency as the bank transfer, to avoid conversion rates.

Generate deposit method

Request an FPS bank deposit method for the chosen account by calling Set up Account Deposit Method with:
  • type: bank
  • network: fps
  • asset: GBP
POST /core/accounts/{accountId}/deposit-method?type=bank&network=fps&asset=GBP
A successful response includes the routing details (sort code + account number) and a unique payment reference.
{
  "depositMethod": {
    "type": "bank",
    "status": "ok",
    "details": {
      "network": "fps",
      "asset": "GBP",
      "sortCode": "309897",
      "accountNumber": "12911460",
      "reference": "UHP-6F2Z-1K9Q"
    }
  }
}

Display deposit instructions

Show the required bank fields for FPS (sort code + account number) and the reference, plus any beneficiary details returned (name/address) if present. Deposits with no reference will be credited to a target account with the user’s default currency (GBP in the UK, EUR in the EU, or USD in all other regions).

Using the Payment Widget

To use the Payment Widget for bank deposits, you must use the Select for Deposit flow.

Select for Deposit flow

The Select for Deposit flow allows the user to select a deposit method, including bank transfer, and displays the necessary instructions to complete the deposit. Please refer to the Select for Deposit guide for a step-by-step tutorial.
Regardless of how the deposit instructions were generated (REST API or Payment Widget), the rest of the process is handled via REST API.

Monitor for the incoming transfer

Prefer webhooks for real-time updates, or fall back to polling if webhooks are not feasible. The transaction typically has:
  • origin.node.type: external-account
  • origin.node.id: The id of the source external account
  • origin.node.ownerId: The id of the user that owns the source external account
  • destination.node.type: account
  • destination.node.id: The id of the destination account
  • destination.node.ownerId: The id of the user that owns the destination account
An external account is created only after a bank deposit posts successfully. If a deposit is rejected or cannot be matched (for example, missing reference), no external account linkage is created.

Sample transaction

Below is a sample transaction object returned by the Get Transaction endpoint. The origin field shows how the source of the deposit is represented:
  {
    "transaction": {
      "id": "b1bbbc0f-dae2-4e94-9e6d-4b9d5a1f3c1f",
      "origin": {
        "asset": "GBP",
        "amount": "250.00",
        "node": {
          "type": "external-account",
          "id": "aa6e6efa-8d73-497c-8278-0347f459bd68",
          "ownerId": "e4ce04dc-67b7-4e9f-af91-482cb6f9fc4a"
        }
      },
      "destination": {
        "asset": "GBP",
        "amount": "250.00",
        "node": {
          "type": "account",
          "id": "a00507fe-628c-4f27-ae81-e1c40b2a8fb8",
          "ownerId": "e4ce04dc-67b7-4e9f-af91-482cb6f9fc4a"
        }
      },
      "status": "completed",
      "quotedAt": "2025-01-10T11:02:39Z",
      "createdAt": "2025-01-10T11:12:39Z",
      "updatedAt": "2025-01-10T11:13:08Z",
      "denomination": {
        "asset": "GBP",
        "amount": "250.00",
        "target": "origin"
      }
    }
  }

Notify the user

Display an in-app confirmation when the transaction is completed, and send an email if applicable.
You now support bank transfer deposits with the Enterprise API Suite.