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

# Create Virtual Account

> Provision a named virtual account in one currency inside a virtual account group

A virtual account is a named bank account in a single currency, opened inside an approved [virtual account group](/fx/api-reference/virtual-account-groups/create-group). It carries its own bank details for receiving deposits and its own balance, which can fund a payout or be a leg of a transfer.

The group must be through onboarding first: `status` `enabled` and `onboarding_status` `approved`. [Create Virtual Account Group](/fx/api-reference/virtual-account-groups/create-group) covers who can open a group and what the account needs to get there: an identity profile, plus an associated person marked as a signer on a business account.

Provisioning is asynchronous. The call returns as soon as Hop has recorded the virtual account and asked the provider to open the underlying bank account: the bank details arrive later.

Requires an API key with `write` access on the `virtual_accounts` scope.

A newly created virtual account comes back with `status: "pending"`, `provisioned_at: null`, and every bank detail field set to `null`: `beneficiary_name`, `bank_name`, `account_number`, `account_number_last4`, `iban`, `swift_bic`, `routing_number`, `routing_number_type`, `bank_address` and `beneficiary_address`. It cannot be paid into yet, so do not publish this response as funding instructions.

Poll [Get Virtual Account](/fx/api-reference/virtual-accounts/get-virtual-account) until `status` is `active`; at that point `provisioned_at` is set and the bank details are filled in. Activation depends on the provider opening the account and is not instant. A virtual account can also stay `pending` indefinitely if the provider never returns payable bank details; contact Hop if one does not activate. Once active, share the bank details returned by Get Virtual Account with the payer. Deposits using those details credit the virtual account's own balance.

## Path Parameters

<ParamField path="account_id" type="string" required>
  The account's external ID (starts with `acct_`)
</ParamField>

<ParamField path="group_id" type="string" required>
  The virtual account group's external ID (starts with `vag_`); see [Create Virtual Account Group](/fx/api-reference/virtual-account-groups/create-group). A group that does not exist on this account returns `404`.
</ParamField>

## Headers

<ParamField header="Idempotency-Key" type="string" required>
  Key for this virtual account, scoped to the account. Canonical lowercase 8-4-4-4-12 UUID, for example `9f8c3a21-4d5e-4b67-8a90-1c2d3e4f5a6b`. See [Idempotency](/fx/api-reference/idempotency).
</ParamField>

Replaying a key that already created a virtual account returns that virtual account with `200`, unchanged, and does not open a second one at the provider or re-charge any creation fee.

Only `group_id` and `currency` are compared on a replay. Reusing a key with a different `currency`, or against a different group, returns `409` `IDEMPOTENCY_CONFLICT` with `details.virtual_account_id` naming the virtual account the key already created. Reusing it with a different `label` is **not** an error: the original virtual account is returned and the new label is ignored, so a label is never changed this way. Use a fresh key to open a differently labelled virtual account.

While a request with the same key is still in flight, a second one fails fast with `409` `IDEMPOTENCY_REQUEST_IN_PROGRESS`. Retry after a short backoff.

## Request Body

<ParamField body="currency" type="string" required>
  Currency of the virtual account, for example `EUR`. The provider backing the group must support opening an account in it; a currency the group's provider has no account profile for is rejected with `502`.
</ParamField>

<ParamField body="label" type="string">
  Optional label for your own reference (max 255 characters). Not shown to payers, not unique, and cannot be changed through this API.
</ParamField>

A group may hold several virtual accounts in the same currency. Each call with a new `Idempotency-Key` opens another one. There is no per-currency limit on this endpoint, so guard against accidental duplicates by reusing your key on retries.

## Response

Returns `201` with the new virtual account, or `200` with the existing one when the `Idempotency-Key` was already used.

<ResponseField name="id" type="string">
  Virtual account identifier (starts with `va_`)
</ResponseField>

<ResponseField name="object" type="string">
  Always returns `"virtual_account"`
</ResponseField>

<ResponseField name="account_id" type="string">
  Account ID (starts with `acct_`). The response does not repeat `group_id`.
</ResponseField>

<ResponseField name="currency" type="string">
  Currency code of the virtual account
</ResponseField>

<ResponseField name="status" type="string">
  Virtual account status: `pending` (created, bank details not yet issued: not payable), `active` (payable and usable as a payout funding source or transfer leg), `frozen` (balance movements blocked by Hop) or `disabled` (closed by Hop; terminal). A create always returns `pending`.
</ResponseField>

<ResponseField name="label" type="string">
  Label, or `null`
</ResponseField>

<ResponseField name="balances" type="object">
  Balance buckets, each a decimal string formatted for the currency. A new virtual account is all zeroes.

  <Expandable title="Balance Fields">
    <ResponseField name="available" type="string">
      Balance free to spend on a payout or transfer
    </ResponseField>

    <ResponseField name="pending_payin" type="string">
      Deposits received but not yet settled
    </ResponseField>

    <ResponseField name="held_for_payout" type="string">
      Amount held for in-flight payouts
    </ResponseField>

    <ResponseField name="total" type="string">
      Total across all buckets
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="beneficiary_name" type="string">
  Account holder a payer should address the deposit to, or `null` while `pending`
</ResponseField>

<ResponseField name="bank_name" type="string">
  Bank holding the account, or `null` while `pending`
</ResponseField>

<ResponseField name="account_number" type="string">
  Full account number, or `null` while `pending` and on accounts identified by IBAN
</ResponseField>

<ResponseField name="account_number_last4" type="string">
  Masked account number, or the masked IBAN when the account has no account number. `null` while `pending`. Despite the name, this field can carry IBAN digits.
</ResponseField>

<ResponseField name="iban" type="string">
  Full IBAN, or `null` while `pending` and on accounts identified by account number
</ResponseField>

<ResponseField name="swift_bic" type="string">
  SWIFT/BIC code, or `null`
</ResponseField>

<ResponseField name="routing_number" type="string">
  Routing number or sort code, or `null`
</ResponseField>

<ResponseField name="routing_number_type" type="string">
  What `routing_number` holds, for example `ROUTING_NUMBER` or `SORT_CODE`; `null` when there is no routing number
</ResponseField>

<ResponseField name="bank_address" type="object">
  Bank address for wire instructions (`address_line`, `city`, `region`, `postal_code`, `country`), or `null`
</ResponseField>

<ResponseField name="beneficiary_address" type="object">
  Account holder address for wire instructions, same shape as `bank_address`, or `null`
</ResponseField>

<ResponseField name="provisioned_at" type="string">
  ISO 8601 timestamp when the bank details became available, or `null` while `pending`
</ResponseField>

<ResponseField name="created" type="string">
  ISO 8601 timestamp when created
</ResponseField>

<ResponseField name="updated" type="string">
  ISO 8601 timestamp when last updated
</ResponseField>

<ResponseField name="created_by" type="string">
  ID of the API key that created the virtual account (starts with `ak_`)
</ResponseField>

<ResponseField name="updated_by" type="string">
  Actor that last updated the virtual account, or `null`
</ResponseField>

Once the virtual account is `active`, its `id` can be passed as a payout `funding_source`, see [Create Payout](/fx/api-reference/payouts/create-payout), or as a leg of a same-currency [transfer](/fx/api-reference/transfers/create-transfer).

## Errors

| Status | `code` | When |
| - | - | - |
| `404` | `RESOURCE_NOT_FOUND` | `group_id` is unknown or belongs to another account |
| `409` | `IDEMPOTENCY_REQUEST_IN_PROGRESS` | Another request with this `Idempotency-Key` is still in flight |
| `409` | `IDEMPOTENCY_CONFLICT` | The key was already used for a different currency or a different group; `details.virtual_account_id` is the existing virtual account |
| `422` | `VALIDATION_ERROR` | `Idempotency-Key` is missing or is not a canonical 8-4-4-4-12 UUID, `currency` is not a supported code, or `label` is longer than 255 characters |
| `422` | `VIRTUAL_ACCOUNT_GROUP_NOT_ENABLED` | The group's onboarding is not approved, or the group is not enabled |
| `422` | `INSUFFICIENT_FUNDS` | The group charges a creation fee and the paying balance does not cover it |
| `422` | `SERVICE_FEE_BALANCE_MISSING` | The group charges a creation fee in a currency the fee-paying account holds no balance in |
| `502` | `GATEWAY_REQUEST_ERROR` | The provider rejected the request: no account profile exists for `currency`, or the group's provider onboarding reference is not valid |

## Request Example

```json theme={null}
{
  "currency": "EUR",
  "label": "EU Collections"
}
```

## Response Example

```json theme={null}
{
  "id": "va_6b3n9k1q4t7w2z5m8p0r3d6f",
  "object": "virtual_account",
  "account_id": "acct_ka44qsvpo8q3wtzuwfqf0h6u",
  "currency": "EUR",
  "status": "pending",
  "label": "EU Collections",
  "balances": {
    "available": "0.00",
    "pending_payin": "0.00",
    "held_for_payout": "0.00",
    "total": "0.00"
  },
  "beneficiary_name": null,
  "bank_name": null,
  "account_number": null,
  "account_number_last4": null,
  "iban": null,
  "swift_bic": null,
  "routing_number": null,
  "routing_number_type": null,
  "bank_address": null,
  "beneficiary_address": null,
  "provisioned_at": null,
  "created": "2026-01-15T10:00:00Z",
  "updated": "2026-01-15T10:00:00Z",
  "created_by": "ak_3f7k9m2p5r8t1v4x6z0b3n5q",
  "updated_by": null
}
```
