---
url: /docs/fiat/virtual-accounts-create.md
description: >-
  Prerequisites, required customer fields, the create request, and the webhooks
  fired when a BlindPay virtual account changes status.
---

Creating a virtual account is a single API call, but the customer behind it must be fully set up first. This page covers the prerequisites, the fields the banking partner requires, the create request itself, and the webhooks that tell you when the account is live.

## Prerequisites

You must also [create a customer](/fiat/quickstart) and add a [blockchain wallet](/stablecoin/blockchain-wallets) before generating a virtual account. The customer's `kyc_status` must already be `approved`: you cannot create a virtual account for a customer still in KYC review. The wallet is where stablecoin settlement lands behind the scenes; see [Blockchain wallets](/stablecoin/blockchain-wallets) for the full mechanics.

## Required fields

### For businesses

Make sure the following fields are filled in on the customer before requesting a virtual account:

* `account_purpose`, `business_type`, `business_description`, `business_industry` (NAICS code), `estimated_annual_revenue`, `source_of_wealth`, `publicly_traded`
* At least one registered business owner, with `ownership_percentage` and `title`
* Owners living in the US must have their SSN in `tax_id`

To update these fields on an existing customer:

```bash [cURL]
curl --request PUT \
  --url https://api.blindpay.com/v1/instances/in_000000000000/customers/re_000000000000 \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "email": "john@doe.com",
    "tax_id": "000000000",
    "country": "US",
    "owners": [
        {
            "ownership_percentage": 50,
            "title": "CTO",
            "id": "ub_000000000000"
        }
    ],
    "account_purpose": "business_expenses",
    "business_type": "corporation",
    "business_description": "description",
    "business_industry": "541511",
    "estimated_annual_revenue": "0_99999",
    "source_of_wealth": "business_dividends_or_profits",
    "publicly_traded": false
}'
```

### For individuals (sole proprietors)

Sole proprietors must include a supporting document in the virtual account creation request itself:

* `sole_proprietor_doc_type`: type of supporting document, one of `master_service_agreement`, `salary_slip`, `bank_statement`
* `sole_proprietor_doc_file`: a URL pointing to the uploaded document

## The request

```bash [Business]
curl --request POST \
  --url https://api.blindpay.com/v1/instances/in_000000000000/customers/re_000000000000/virtual-accounts \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "banking_partner": "cfsb",
  "token": "USDC",
  "blockchain_wallet_id": "bw_000000000000"
}'
```

```bash [Sole proprietor]
curl --request POST \
  --url https://api.blindpay.com/v1/instances/in_000000000000/customers/re_000000000000/virtual-accounts \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "banking_partner": "cfsb",
  "token": "USDC",
  "blockchain_wallet_id": "bw_000000000000",
  "sole_proprietor_doc_type": "master_service_agreement",
  "sole_proprietor_doc_file": "https://example.com/document.pdf"
}'
```

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `banking_partner` | string | Yes | The example value is `cfsb`. Available values depend on your instance and region. |
| `token` | `USDC`, `USDT`, `USDB` | Yes | Settlement stablecoin. `USDT` requires the linked wallet's network to be Polygon, Ethereum, or Solana. `USDB` is development-only. |
| `blockchain_wallet_id` | string (`bw_...`) | Yes | Must belong to the same customer. |
| `sole_proprietor_doc_type` | `master_service_agreement`, `salary_slip`, `bank_statement` | Conditional | Required for individual customers on this banking partner. |
| `sole_proprietor_doc_file` | URL string | Conditional | Required alongside `sole_proprietor_doc_type`. |

The response includes `id` (`va_...`), `banking_partner`, `kyc_status`, `token`, `blockchain_wallet_id`, and a `us` object with `ach`, `wire`, and `rtp` sub-objects (each `{routing_number, account_number}`), plus SWIFT and receiving-bank details once the account is `approved`. Before approval, rail numbers are empty.

You can update `token` and `blockchain_wallet_id` on an existing virtual account; `banking_partner` cannot be changed after creation.

## Webhooks

| Event | Fires when |
| --- | --- |
| `virtualAccount.new` | A virtual account is successfully created (every status path, including development). |
| `virtualAccount.complete` | The banking partner confirms the account and `kyc_status` flips to `approved`. Rail numbers are populated in the payload. |

See [Webhooks](/learn/webhooks) for signature verification and delivery details.

## Related

* [Virtual accounts](/fiat/virtual-accounts): what it is, account types, and statuses
* [Bank transfer in](/fiat/receive): how deposits into the account become payins
* [Blockchain wallets](/stablecoin/blockchain-wallets): where settlement lands
* [Knowledge base: virtual account requirements](/kb/virtual-accounts): source of funds and source of wealth documents
