---
url: /docs/virtual-accounts-create.md
description: >-
  Create a virtual account with the BlindPay API, from customer prerequisites
  and required compliance fields to the webhooks that confirm approval.
---

The request is one API call. The work is in the setup. The customer needs approved KYC, the compliance fields the banking partner checks, and a linked wallet for settlement. This page goes through each step in order and ends with the webhooks that tell you the account is live.

## Prerequisites

You must also [create a customer](/quickstart-payin) and add a [blockchain wallet](/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](/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`

Depending on the banking partner, the API rejects the creation request with a `missing_required_fields` error naming each blank field, so fill these in before calling the endpoint rather than relying on manual review to catch them.

You can find the `business_industry` NAICS code list at api.blindpay.com/reference.

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)

Individual customers need `account_purpose` and `source_of_wealth` filled in on the customer, subject to the same `missing_required_fields` rejection as businesses.

Sole proprietors must also 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

After creation, a production virtual account starts in `pending_review` status. It moves through compliance and bank review before becoming `approved`. You'll get a webhook notification at each transition.

```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](/virtual-accounts): what it is, account types, and statuses
* [Bank transfer in](/payins): how deposits into the account become payins
* [Blockchain wallets](/blockchain-wallets): where settlement lands
* [Knowledge base: virtual account requirements](/kb/virtual-accounts): source of funds and source of wealth documents
