> ## Documentation Index
> Fetch the complete documentation index at: https://docs.conduit.financial/llms.txt
> Use this file to discover all available pages before exploring further.

# Add Virtual Accounts

> Request Virtual Accounts and retrieve deposit instructions

The customer must already be onboarded and active before you add a feature. You get `customerId` from the `application.approved` webhook — see [Onboard a Customer](/guides/onboard-customer) and [The Onboarding Lifecycle](/guides/onboarding-lifecycle). Calling features before the customer is active returns [`CUSTOMER_NOT_ONBOARDED`](/errors/customer-not-onboarded).

<Note>
  **Test this flow in sandbox.** Drive it end-to-end with simulated money and deterministic controls — start with the [sandbox quickstart](/sandbox/quickstart), then [deposit simulation](/sandbox/deposits) for this flow, and the [cheat sheet](/sandbox/cheat-sheet) for every magic value and simulate endpoint.
</Note>

## Flow

Add Virtual Accounts by submitting a feature application against an existing customer. The integration:

1. Discover the customer's feature requirements.
2. Submit a `virtual_account` feature application.
3. Listen for `application.approved` or `application.rejected` webhooks (with `applicationType: "virtual_account"`).
4. Listen for `virtual_account.activated`.
5. Fetch the customer's Virtual Accounts and read `balances[]` plus `depositInstructions[]`.

## Request The Feature

Discover any extra requirements for the customer:

```bash theme={null}
curl 'https://api.conduit.financial/v2/customers/{customerId}/features/requirements?type=virtual_account' \
  -H "x-api-key: YOUR_API_KEY"
```

Like the onboarding flow, the discovery response lists any required `fields` and `documents`. When present, collect them and include them in the submit body below. When the response asks for nothing extra, submit with just `type` and `asset`.

Add `&asset=USD` to discover the requirements for one currency only. Requirements can differ by currency, so pass the currency you are about to submit. When no banking partner can hold the currency you pass, this call returns [`NO_ELIGIBLE_PROVIDER`](/errors#no-eligible-provider) instead of a requirements response — omit `asset` to see which currencies are available to the customer. Virtual accounts are offered in USD, EUR and GBP, and an individual customer can hold any of the three. A request for MXN or TRY returns `422 NO_ELIGIBLE_PROVIDER`.

Submit the Virtual Account feature application for the target asset. `Idempotency-Key` is required — without it the call returns `IDEMPOTENCY_KEY_REQUIRED`.

```bash theme={null}
curl https://api.conduit.financial/v2/customers/{customerId}/features \
  -X POST \
  -H "x-api-key: YOUR_API_KEY" \
  -H "content-type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "type": "virtual_account",
    "asset": { "code": "USD" }
  }'
```

The endpoint returns `202 Accepted` with a Virtual Account feature application carrying its own application id. Track it through the `application.approved` and `application.rejected` webhooks with `applicationType: "virtual_account"`. A rejection carries `resubmittable`: when it is `true`, correct the data and request the feature again; when it is `false`, the decision is final.

A customer can hold one Virtual Account per currency. Submit the request again with a different `asset` to add another currency. A second request for a currency the customer already has returns [`FEATURE_ALREADY_EXISTS`](/errors#feature-already-exists), and the message names that currency.

<Note>
  Pass your own `clientReferenceId` in the request body to correlate the
  resulting application with a record in your system. It is echoed back on
  the response, and `GET /v2/applications?clientReferenceId=` finds the
  application again. Reusing a reference for a currency the customer
  already has returns
  [`FEATURE_ALREADY_EXISTS`](/errors#feature-already-exists) first.
  Otherwise, [`CONFLICT`](/errors#conflict) fires whenever any non-rejected,
  non-cancelled application anywhere in your organization already holds
  that reference — even one you submitted for the same customer under a
  different feature, such as a crypto wallet.
</Note>

## Listen For Activation

Once the feature application is approved, listen for `virtual_account.activated` to know the Virtual Account is usable. Deposit instructions populate once the Virtual Account's `status` is `active`. See [Webhooks](/webhooks) and [Virtual Accounts](/concepts/virtual-accounts).

## Retrieve Deposit Instructions

After activation, fetch the Virtual Accounts for the customer. See [Virtual Accounts](/concepts/virtual-accounts) for the full deposit-block union.

```bash theme={null}
curl https://api.conduit.financial/v2/customers/{customerId}/virtual-accounts \
  -H "x-api-key: YOUR_API_KEY"
```

The response contains VA-level balances and deposit instructions. It does not include provider bank accounts.

```json theme={null}
{
  "data": [
    {
      "id": "vac_...",
      "asset": { "code": "USD" },
      "status": "active",
      "balances": [
        {
          "available": { "code": "USD", "amount": "0.00" },
          "pending": { "code": "USD", "amount": "0.00" },
          "frozen": { "code": "USD", "amount": "0.00" }
        }
      ],
      "depositInstructions": [
        {
          "type": "us_domestic",
          "currency": "USD",
          "accountNumber": "123456789",
          "beneficiaryName": "Example Customer",
          "beneficiaryAddress": "1 Main St\nNew York, NY, 10001\nUSA",
          "beneficiaryPostalAddress": {
            "addressLine1": "1 Main St",
            "city": "New York",
            "state": "NY",
            "postalCode": "10001",
            "country": "US"
          },
          "bank": {
            "legalName": "Example Bank",
            "address": "2 Bank St, New York, NY",
            "postalAddress": {
              "addressLine1": "2 Bank St",
              "city": "New York",
              "state": "NY",
              "postalCode": "10005",
              "country": "US"
            }
          },
          "rails": [
            { "rail": "ach", "routingNumber": "021000021", "sameDayEligible": false },
            { "rail": "fedwire", "routingNumber": "021000021" },
            { "rail": "rtp", "routingNumber": "021000021", "networks": ["tch"] }
          ]
        }
      ],
      "activatedAt": "2026-04-30T00:00:00.000Z",
      "createdAt": "2026-04-30T00:00:00.000Z",
      "updatedAt": "2026-04-30T00:00:00.000Z"
    }
  ],
  "meta": {
    "mode": "cursor",
    "nextCursor": null,
    "previousCursor": null,
    "total": 1
  }
}
```

A EUR Virtual Account returns a `sepa` block on the same endpoint. Send to the `iban` and the `bic`:

```json theme={null}
{
  "type": "sepa",
  "currency": "EUR",
  "iban": "DE89370400440532013000",
  "bic": "EXMPDEFFXXX",
  "beneficiaryName": "Example Customer",
  "rails": [{ "rail": "sepa" }]
}
```

Retrieve one Virtual Account when you have its id:

```bash theme={null}
curl https://api.conduit.financial/v2/customers/{customerId}/virtual-accounts/{virtualAccountId} \
  -H "x-api-key: YOUR_API_KEY"
```

<Warning>
  The block `type` values were previously `domestic_usd` and `swift_usd`, and blocks carried no `currency` field. If your integration branches on the old names, switch to `us_domestic` and `swift` and read the currency from `currency`.
</Warning>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.