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

# Virtual accounts guide

# Virtual Accounts

## Overview

**Virtual Accounts** are Nigerian bank accounts that automatically convert NGN deposits into cNGN tokens. StRails generates these accounts through licensed payment providers, enabling seamless fiat-to-crypto onramp for both fintechs and their users.

## Key Characteristics

| Feature         | Description                               |
| --------------- | ----------------------------------------- |
| **Currency**    | Nigerian Naira (NGN)                      |
| **Conversion**  | Auto-converts deposits to cNGN            |
| **Providers**   | Licensed Nigerian payment gateways        |
| **Validation**  | BVN-based depositor verification          |
| **Destination** | Funds go to [Smart Wallet](/smart-wallet) |

## How Virtual Accounts Work

```
┌─────────────────────────────────────────────────────────────┐
│                     Deposit Flow                            │
└─────────────────────────────────────────────────────────────┘

Bank Transfer (NGN)
        │
        ▼
┌─────────────────────┐
│  Virtual Account    │ ← Generated by StRails
│  (Nigerian Bank)    │
└─────────────────────┘
        │
        ▼
┌─────────────────────┐
│  Payment Gateway    │ ← Validates deposit, notifies StRails
└─────────────────────┘
        │
        ▼
┌─────────────────────┐
│  BVN Validation     │ ← Depositor name must match BVN
└─────────────────────┘
        │
        ├── ✅ Match
        │       │
        │       ▼
        │   ┌─────────────────────┐
        │   │  Mint cNGN          │ → Destination Wallet (varies by account type)
        │   └─────────────────────┘
        │
        └── ❌ Mismatch
                │
                ▼
            ┌─────────────────────┐
            │  Refund to Sender   │
            └─────────────────────┘
```

## Virtual Account Types

StRails uses three types of virtual accounts:

| Type                      | Lifetime   | Purpose                               | cNGN Destination                           |
| ------------------------- | ---------- | ------------------------------------- | ------------------------------------------ |
| **Fintech Permanent**     | Permanent  | Fintech's own funding account         | Fintech's default wallet (HSM or external) |
| **User Permanent**        | Permanent  | User's dedicated funding account      | Strails-controlled HSM wallet              |
| **Transaction Temporary** | 30 minutes | One-time funding with specific amount | [Smart Wallet](/smart-wallet)              |

***

## Fintech Permanent Virtual Account

Your fintech receives a dedicated bank account during onboarding. This is your primary channel for receiving NGN deposits.

### Characteristics

* **Never expires** — always available for deposits
* **No amount limit** — accept any deposit amount
* **Auto-mints cNGN** — deposits convert to tokens in your **default wallet** (Strails-controlled HSM wallet or your whitelisted external wallet)
* **Webhook notifications** — receive `fintech.virtual_account.deposit.received` events

### How to Retrieve

```bash theme={null}
curl -X GET https://beta.stablesrail.io/v1/getfintechvirtualaccount \
  -H "x-api-key: YOUR_API_KEY"
```

**Response:**

```json theme={null}
{
  "data": {
    "virtualAccount": {
      "accountNumber": "1234567890",
      "accountName": "Example Fintech Ltd",
      "bankName": "ALP Bank",
      "status": "active"
    }
  }
}
```

### Deposit Flow

```
NGN Deposit → Fintech Virtual Account
        │
        ▼
   Payment Gateway confirms
        │
        ▼
   Mint cNGN → Fintech's Default Wallet
        │        (Strails-controlled HSM wallet
        │         OR whitelisted external wallet)
        ▼
   Webhook: fintech.virtual_account.deposit.received
```

***

## User Permanent Virtual Account

After successful BVN verification, each user automatically receives a permanent virtual account. This enables direct deposits without creating new accounts for each transaction.

### Characteristics

* **Never expires** — users can deposit anytime
* **BVN-linked** — deposits must come from the user's own bank account
* **Auto-mints cNGN** — deposits convert to tokens in user's **Strails-controlled HSM wallet** ([Multi-Chain Wallet](/multi-chain-wallet))
* **Name validation** — depositor name must match BVN records

### How to Retrieve

User permanent accounts are returned in the `/getuserdetails` response:

```bash theme={null}
curl -X POST https://beta.stablesrail.io/v1/getuserdetails \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"userId": "user_uniqueID"}'
```

**Response:**

```json theme={null}
{
  "data": {
    "userId": "user_uniqueID",
    "virtualAccounts": [
      {
        "accountNumber": "1234567890",
        "accountName": "John Doe",
        "bankName": "Optimus Bank",
        "accountType": "user_permanent",
        "createdAt": "2026-05-29T12:42:45.034Z"
      }
    ]
  }
}
```

### Deposit Flow

```
User deposits NGN → Permanent Virtual Account
         │
         ▼
   BVN name validation
         │
         ├── ✅ Name matches BVN
         │       │
         │       ▼
         │   Mint cNGN → User's Strails-controlled HSM Wallet
         │       │
         │       ▼
         │   Webhook: fintech.user.deposit.funding.completed
         │
         └── ❌ Name mismatch
                 │
                 ▼
             Refund to sender's bank account
                 │
                 ▼
             Webhook: fintech.user.deposit.funding.refunded
```

### BVN Validation Rules

| Scenario                             | Result                  |
| ------------------------------------ | ----------------------- |
| Deposit from user's own bank account | ✅ Accepted, cNGN minted |
| Deposit from third-party account     | ❌ Refunded              |
| Deposit from corporate account       | ❌ Refunded              |
| Name spelling variation (minor)      | ✅ Usually accepted      |
| Completely different name            | ❌ Refunded              |

***

## Transaction Temporary Virtual Account

Created on-demand for one-time funding operations with specific amounts. Use these when you need precise control over the deposit amount and want optional auto-swap to USDC/USDT.

### Characteristics

* **Expires after 30 minutes** — one-time use only
* **Amount-specific** — user must pay the exact amount shown
* **Includes fees** — total amount includes StRails and fintech fees
* **cNGN destination** — minted to a generated [Smart Wallet](/smart-wallet) (can be controlled by your EOA if `owner` is set)
* **Auto-swap support** — can convert NGN → cNGN → USDC/USDT in one flow
* **Sweep support** — can route funds to user's default wallet

### How to Create

Use the [`/cngnonramp`](/api-reference/transactions#user-onramp-user-wallet-funding) endpoint:

```bash theme={null}
curl -X POST https://beta.stablesrail.io/v1/cngnonramp \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user_uniqueID",
    "amount": 5000,
    "autoSwap": true,
    "assetSwap": "USDC"
  }'
```

**Response:**

```json theme={null}
{
  "data": {
    "requestId": "uuid",
    "walletAddress": "0x...",
    "message": "Pay 5085.00 naira to the bank account. Account expires in 30 minutes.",
    "feeBreakdown": {
      "baseAmount": 5000,
      "totalFee": 85.00,
      "totalAmount": 5085.00
    }
  }
}
```

### How to Retrieve Details

Use the [`/getvirtualaccount`](/api-reference/virtual-accounts#retrieve-a-specific-virtual-account-with-details-about-amounts-and-fees) endpoint with the `requestId`:

```bash theme={null}
curl -X POST https://beta.stablesrail.io/v1/getvirtualaccount \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"requestId": "uuid"}'
```

### Deposit Flow with Auto-Swap

```
User pays exact amount → Temporary Virtual Account
         │
         ▼
   Validate amount matches
         │
         ├── ✅ Amount matches
         │       │
         │       ▼
         │   Mint cNGN → Generated Smart Wallet
         │       │
         │       ▼ (if autoSwap=true)
         │   Swap cNGN → USDC
         │       │
         │       ▼ (if sweepToOfframp=true)
         │   Transfer USDC → User's Default Wallet
         │
         └── ❌ Amount mismatch / Expired
                 │
                 ▼
             Refund to sender
```

### Auto-Swap Configuration

| `autoSwap` | `sweepToOfframp` | Result                                       |
| ---------- | ---------------- | -------------------------------------------- |
| `false`    | `false`          | cNGN stays in generated Smart Wallet         |
| `false`    | `true`           | cNGN → user's default wallet                 |
| `true`     | `false`          | cNGN → USDC, stays in generated Smart Wallet |
| `true`     | `true`           | cNGN → USDC → user's default wallet          |

***

## Fee Structure

Virtual account deposits include fees:

| Fee Type        | Description                 | Typical Range |
| --------------- | --------------------------- | ------------- |
| **StRails Fee** | Platform fee for processing | 1-2%          |
| **Fintech Fee** | Your custom fee (optional)  | Configurable  |
| **Gateway Fee** | Payment provider fee        | Varies        |

**Example Breakdown:**

```json theme={null}
{
  "feeBreakdown": {
    "userRequestedAmount": 5000,
    "fintechFeeAmount": 50,
    "fintechFeePercentage": 1.0,
    "strailsFeeAmount": 85,
    "strailsFeePercentage": 1.7,
    "totalFeeAmount": 135,
    "finalAmount": 5135,
    "amountToWallet": 5000
  }
}
```

Configure your fintech fees via the [Fee Management API](/api-reference/fee-management-api).

***

## Webhook Events

Virtual account operations trigger these webhooks:

| Event                                      | Description                                 |
| ------------------------------------------ | ------------------------------------------- |
| `fintech.virtual_account.deposit.received` | Fintech permanent account received deposit  |
| `fintech.user.deposit.received`            | User permanent account received deposit     |
| `fintech.user.deposit.funding.completed`   | User deposit converted to cNGN successfully |
| `fintech.user.deposit.funding.refunded`    | User deposit refunded (BVN mismatch)        |

See [Webhook Events](/api-reference/webhook-events) for payload details.

***

## Best Practices

### For User Permanent Accounts

1. **Display account details clearly** — show account number, bank name, and account name
2. **Remind users about BVN matching** — deposits must come from their own bank account
3. **Handle refund webhooks** — notify users when deposits are refunded due to name mismatch

### For Transaction Temporary Accounts

1. **Show countdown timer** — accounts expire after 30 minutes
2. **Display exact amount** — users must pay the precise amount shown
3. **Provide requestId tracking** — enable users to check status via `/getvirtualaccount`

### General

1. **Always handle webhooks** — don't rely solely on polling
2. **Validate deposits before crediting** — wait for `funding.completed` webhook
3. **Handle partial payments** — temporary accounts may refund if amount doesn't match

***

## Related Documentation

* [Virtual Accounts API](/api-reference/virtual-accounts) — API endpoints reference
* [Transactions API](/api-reference/transactions) — Onramp and offramp operations
* [User Management](/api-reference/user-management) — User onboarding
* [Smart Wallet](/smart-wallet) — Where deposited funds are minted
* [Webhook Events](/api-reference/webhook-events) — Deposit notifications
* [Fee Management](/api-reference/fee-management-api) — Configure your fees
