Virtual accounts

Understanding Virtual Accounts

Virtual accounts are dedicated banking deposit addresses assigned to a customer. They allow your customers to receive fiat payments in USD or EUR, which are automatically converted into stablecoins and delivered to the customer's wallet.

Each virtual account is linked to a specific customer and a destination wallet. Once approved, the account exposes banking details (such as account number, routing number, IBAN, etc.) that the customer can use to send fiat funds. The moment a deposit lands, UnblockPay converts it and delivers the stablecoin balance on-chain.

To create a virtual account, you must first create a Customer and a Wallet.

Virtual account statuses

Status

Description

under_review

The virtual account has been created and is pending approval. Deposit addresses are not yet available.

approved

The virtual account is active. Deposit addresses are populated and ready to receive funds.

rejected

The virtual account was not approved. Contact support for details.

Deposit addresses (deposit_addresses) are only populated in the response when status is approved.

Payment rails by currency

The sender.currency you choose at creation determines which payment rails will be available once the account is approved:

Currency

Available rails

USD

ACH, Wire, SWIFT

EUR

SEPA

Third-party deposits

Set accepts_third_party_payments: true if the EUR virtual account should accept deposits from senders other than the customer themselves (e.g. a business receiving payments from their own clients). If omitted or set to false, only deposits from the account holder will be accepted.

This field is not sent or returned for USD accounts.

Note: Only one EUR virtual account with accepts_third_party_payments: true can exist per customer. Attempting to create a second one will return a 422 eur_virtual_account_3pp_exists error.


How to create a Virtual Account

Example for a EUR account

Request

curl --request POST \
  --url https://api.unblockpay.com/v1/virtual-accounts \
  --header 'Content-Type: application/json' \
  --header 'api-key: <your-api-key>' \
  --data '{
    "customer_id": "019d7904-ec72-7754-b5f5-443defb20da0",
    "name": "EUR Receiving Account",
    "sender": {
      "currency": "EUR"
    },
    "receiver": {
      "currency": "USDC",
      "wallet_id": "019d8001-0000-7000-8000-aabbccddeeff"
    },
    "accepts_third_party_payments": true
  }'

Response

{
  "id": "019e1234-0000-7000-8000-aabbccddeeff",
  "customer_id": "019d7904-ec72-7754-b5f5-443defb20da0",
  "name": "EUR Receiving Account",
  "status": "under_review",
  "sender": {
    "currency": "EUR"
  },
  "receiver": {
    "wallet_id": "019d8001-0000-7000-8000-aabbccddeeff",
    "currency": "USDC",
    "payment_rail": "ethereum",
    "address": "0x742d35Cc6634C0532925a3b8D4C9C0B05f0A5E7c"
  },
  "deposit_addresses": {},
  "accepts_third_party_payments": true,
  "created_at": "2024-03-15T14:30:00Z",
  "updated_at": "2024-03-15T14:30:00Z"
}

Once the virtual account is approved, the deposit_addresses object will be populated with the banking details corresponding to the sender.currency.

{
  "deposit_addresses": {
    "sepa": {
      "iban": "DE89370400440532013000",
      "bic": "COBADEFFXXX",
      "bank_name": "Example Bank",
      "bank_address": "123 Musterstraße, Berlin, 10115",
      "beneficiary_name": "John Doe"
    }
  }
}

Example for a USD account

Request

curl --request POST \
  --url https://api.unblockpay.com/v1/virtual-accounts \
  --header 'Content-Type: application/json' \
  --header 'api-key: <your-api-key>' \
  --data '{
    "customer_id": "019d7904-ec72-7754-b5f5-443defb20da0",
    "name": "My USD Virtual Account",
    "sender": {
      "currency": "USD"
    },
    "receiver": {
      "currency": "USDC",
      "wallet_id": "019d8001-0000-7000-8000-aabbccddeeff"
    }
  }'

Response

{
  "id": "019e1234-0000-7000-8000-aabbccddeeff",
  "customer_id": "019d7904-ec72-7754-b5f5-443defb20da0",
  "name": "My USD Virtual Account",
  "status": "under_review",
  "sender": {
    "currency": "USD"
  },
  "receiver": {
    "wallet_id": "019d8001-0000-7000-8000-aabbccddeeff",
    "currency": "USDC",
    "payment_rail": "solana",
    "address": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
  },
  "deposit_addresses": {},
  "created_at": "2024-03-15T14:30:00Z",
  "updated_at": "2024-03-15T14:30:00Z"
}

Once the virtual account is approved, the deposit_addresses object will be populated with the banking details corresponding to the sender.currency.

{
  "deposit_addresses": {
    "ach": {
      "bank_account_number": "123456789",
      "bank_name": "Example Bank",
      "bank_address": "123 Main St, New York, NY 10001",
      "routing_number": "987654321",
      "beneficiary_name": "John Doe"
    },
    "wire": {
      "bank_account_number": "123456789",
      "bank_name": "Example Bank",
      "bank_address": "123 Main St, New York, NY 10001",
      "routing_number": "987654321",
      "beneficiary_name": "John Doe"
    },
    "swift": {
      "bank_account_number": "123456789",
      "bank_name": "Example Bank",
      "bank_address": "123 Main St, New York, NY 10001",
      "bic": "EXAMUS33XXX",
      "beneficiary_name": "John Doe"
    }
  }
}

Sandbox behavior

In the sandbox environment, virtual accounts are automatically approved upon creation. The status in the response will be approved and the deposit_addresses will already be populated with mock banking details matching the sender.currency.


Virtual Accounts API endpoints

Method

Endpoint

Description

POST

/v1/virtual-accounts

Create a new virtual account for a customer

GET

/v1/virtual-accounts

List all virtual accounts (supports pagination and filtering by customer_id)

GET

/v1/virtual-accounts/{id}

Retrieve a virtual account by ID

For the full schema and request/response details, see the API Reference.