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

# Create Virtual Account

> POST /api/v1/virtual-accounts - Create a tenant-scoped virtual account

Create a dedicated virtual account for a customer.

## Endpoint

```
POST /api/v1/virtual-accounts
```

## Request Body

<ParamField body="customer_id" type="string" required>
  ID of the customer this account belongs to.
</ParamField>

<ParamField body="account_name" type="string" required>
  Name to display on the account.
</ParamField>

<ParamField body="provider" type="string">
  Optional provider override. If omitted, the gateway defaults this to `auto` and internal routing selects the provider.
</ParamField>

<ParamField body="account_type" type="string">
  Optional account type. Supported values depend on your tenant configuration. Common values are `standard` and `dedicated`.
</ParamField>

<ParamField body="metadata" type="object">
  Optional metadata stored with the account.
</ParamField>

<ParamField body="bvn" type="string">
  Optional BVN when required by your provider or compliance flow.
</ParamField>

<ParamField body="country" type="string">
  Optional ISO country code used for provider routing.
</ParamField>

<ParamField body="currency" type="string">
  Optional currency code used for provider routing.
</ParamField>

<ParamField body="request_id" type="string">
  Optional request correlation ID.
</ParamField>

<ParamField body="preferred_bank" type="string">
  Optional preferred bank hint for providers that support bank selection.
</ParamField>

## Response Fields

<ResponseField name="id" type="string">
  Unique identifier for the virtual account.
</ResponseField>

<ResponseField name="tenant_id" type="string">
  Tenant that owns the account.
</ResponseField>

<ResponseField name="customer_id" type="string">
  Customer assigned to the account.
</ResponseField>

<ResponseField name="account_number" type="string">
  Account number returned by the underlying provider.
</ResponseField>

<ResponseField name="account_name" type="string">
  Name on the account.
</ResponseField>

<ResponseField name="bank_code" type="string">
  Provider bank code or slug.
</ResponseField>

<ResponseField name="bank_name" type="string">
  Human-readable bank name.
</ResponseField>

<ResponseField name="provider" type="string">
  Provider selected for the account.
</ResponseField>

<ResponseField name="status" type="string">
  Current account status. Typical values are `active`, `frozen`, `closed`, or `expired`.
</ResponseField>

<ResponseField name="account_type" type="string">
  Account type stored by the service.
</ResponseField>

<ResponseField name="total_received_minor" type="number">
  Total inbound value recorded on the account, in minor units.
</ResponseField>

<ResponseField name="transaction_count" type="number">
  Number of inbound credits recorded on the account.
</ResponseField>

<ResponseField name="metadata" type="object | null">
  Account metadata.
</ResponseField>

<ResponseField name="expires_at" type="string | null">
  Provider expiry time when the allocated account is time-bound.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 creation timestamp.
</ResponseField>

<ResponseField name="updated_at" type="string">
  ISO 8601 last-update timestamp.
</ResponseField>

## Example Request

<CodeGroup>
  ```javascript JavaScript theme={null}
  const account = await client.virtualAccounts.create({
    customerId: 'cus_1234567890',
    accountName: 'John Doe Collections',
    preferredBank: 'wema-bank',
    metadata: {
      purpose: 'collections',
      tier: 'premium'
    }
  });

  console.log(account.accountNumber);
  ```

  ```python Python theme={null}
  account = client.virtual_accounts.create(
      customer_id='cus_1234567890',
      account_name='John Doe Collections',
      preferred_bank='wema-bank',
      metadata={
          'purpose': 'collections',
          'tier': 'premium'
      }
  )

  print(account.account_number)
  ```

  ```php PHP theme={null}
  $account = $client->virtualAccounts->create([
      'customer_id' => 'cus_1234567890',
      'account_name' => 'John Doe Collections',
      'preferred_bank' => 'wema-bank',
      'metadata' => [
          'purpose' => 'collections',
          'tier' => 'premium'
      ]
  ]);

  print_r($account);
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.usezentra.com/api/v1/virtual-accounts \
    -H "Authorization: Bearer YOUR_SECRET_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "customer_id": "cus_1234567890",
      "account_name": "John Doe Collections",
      "preferred_bank": "wema-bank",
      "metadata": {
        "purpose": "collections",
        "tier": "premium"
      }
    }'
  ```
</CodeGroup>

## Example Response

```json theme={null}
{
  "data": {
    "id": "va_abc123xyz",
    "tenant_id": "tenant_123",
    "customer_id": "cus_1234567890",
    "account_number": "0123456789",
    "account_name": "John Doe Collections",
    "bank_code": "wema-bank",
    "bank_name": "Wema Bank",
    "provider": "paystack_dva",
    "status": "active",
    "account_type": "standard",
    "total_received_minor": 0,
    "transaction_count": 0,
    "metadata": {
      "purpose": "collections",
      "tier": "premium"
    },
    "expires_at": null,
    "created_at": "2026-03-07T10:30:00Z",
    "updated_at": "2026-03-07T10:30:00Z"
  },
  "meta": {
    "timestamp": "2026-03-07T10:30:00Z",
    "requestId": "req_123"
  }
}
```

## Error Responses

<ResponseExample>
  ```json Validation Error theme={null}
  {
    "error": {
      "code": "VALIDATION_ERROR",
      "message": "customer_id is required",
      "details": null
    },
    "meta": {
      "path": "/api/v1/virtual-accounts",
      "method": "POST"
    }
  }
  ```

  ```json Provider Routing Failure theme={null}
  {
    "error": {
      "code": "FAILED_PRECONDITION",
      "message": "Provider routing unavailable",
      "details": null
    },
    "meta": {
      "path": "/api/v1/virtual-accounts",
      "method": "POST"
    }
  }
  ```
</ResponseExample>

## Notes

<Info>
  `bank_code` is an output field. The reviewed create endpoint does not require a bank code; use `preferred_bank` only when you need to bias provider selection.
</Info>

<Note>
  Amounts are always returned in integer minor units. For virtual accounts, the running inbound total is `total_received_minor`.
</Note>

## Next Steps

<CardGroup cols={2}>
  <Card title="Get Account" icon="eye" href="/api-reference/virtual-accounts/get">
    Retrieve account details
  </Card>

  <Card title="List Accounts" icon="list" href="/api-reference/virtual-accounts/list">
    View all accounts
  </Card>

  <Card title="Handle Webhooks" icon="webhook" href="/api-reference/webhooks/overview">
    Process account credits
  </Card>

  <Card title="Close Account" icon="xmark" href="/api-reference/virtual-accounts/close">
    Close an account
  </Card>
</CardGroup>
