Virtual Accounts

Overview

Virtual accounts are bank accounts that can operate in two modes:

  1. Crypto Mode: Automatically convert incoming deposits into cryptocurrency and settle funds to a designated blockchain wallet
  2. Fiat Mode: Maintain a USD balance without automatic conversion

Each virtual account has unique deposit instructions (bank account details) that can be used to receive transfers.

Prerequisites

Before creating a virtual account, ensure:

  1. User has been created
  2. User has completed identity verification (KYC/KYB)
  3. User status is VERIFIED
  4. For Crypto Mode: User has a blockchain wallet (Solana or Polygon)
  5. For Fiat Mode: No wallet required at creation time

Create Virtual Account

Endpoint

POST /v1/virtual-accounts

Headers

HeaderRequiredDescription
AuthorizationYesBearer token from authentication
x-api-keyYesYour API key
Content-TypeYesMust be application/json
idempotency-keyYesUnique identifier to prevent duplicate creation

Request Body

{
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "US_BANK",
  "bank": "portage",
  "destination": {
    "currency": "USDC",
    "network": "solana",
    "address": "7tQJvFk8XZoaVRjLGcBdqN3hJq8VqBvGpPQy8R9xYwZ1"
  },
  "description": "Primary deposit account"
}

Request Fields

FieldTypeRequiredDescription
user_idstring (UUID)YesThe user who will own this virtual account
typestringYesAccount type: US_BANK
bankstringYesBank provider: portage or slovak_savings_bank
destinationobjectConditionalRequired for crypto mode. Omit for fiat mode.
destination.currencystringConditionalCryptocurrency: USDC or USDT (required if destination provided)
destination.networkstringConditionalBlockchain: solana or polygon (required if destination provided)
destination.addressstringConditionalWallet address on the specified blockchain (required if destination provided)
descriptionstringNoHuman-readable description of the account

Account Modes

ModeDestination FieldResponse modeDescription
CryptoRequired"crypto"Deposits auto-convert to crypto and settle to wallet
FiatOmit"fiat"Deposits held in USD, initiate payouts when needed

Important: The account mode is determined at creation and cannot be changed later. The mode field is included in all API responses.

Bank Options

BankDescriptionFiat Mode SupportedUse For
portagePortage BankYesNon-US based users
austin_capital_trustAustin Capital TrustYesUS-based users
slovak_savings_bankSlovak Savings BankYesSandbox only

Deprecated: lead_bank is no longer supported. Existing accounts remain functional, but new accounts cannot be created with this bank.

Sandbox: Only slovak_savings_bank is available in the sandbox environment.

Production: Use portage for non-US based users and austin_capital_trust for US-based users.

Supported Configurations

CurrencyNetwork
USDCSolana
USDCPolygon
USDTSolana
USDTPolygon
USDTTron

Address Validation

Wallet addresses are validated based on the network:

NetworkFormatExample
SolanaBase58, 32-44 characters7tQJvFk8XZoaVRjLGcBdqN3hJq8VqBvGpPQy8R9xYwZ1
Polygon0x + 40 hex characters0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1

Example: Create US_BANK Account

curl -X POST https://api.balampay.com/v1/virtual-accounts \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "idempotency-key: va-creation-$(uuidgen)" \
  -d '{
    "user_id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "US_BANK",
    "bank": "portage",
    "destination": {
      "currency": "USDC",
      "network": "solana",
      "address": "7tQJvFk8XZoaVRjLGcBdqN3hJq8VqBvGpPQy8R9xYwZ1"
    },
    "description": "Main receiving account for freelance payments"
  }'

Response (US_BANK)

{
  "id": "550e8400-e29b-41d4-a716-446655440001",
  "status": "activating",
  "type": "US_BANK",
  "bank": "portage",
  "mode": "crypto",
  "destination": {
    "currency": "USDC",
    "network": "solana",
    "address": "7tQJvFk8XZoaVRjLGcBdqN3hJq8VqBvGpPQy8R9xYwZ1"
  },
  "source_deposit_instructions": null,
  "description": "Main receiving account for freelance payments",
  "created_at": "2024-01-15T10:30:00Z"
}

Note: US_BANK accounts are created asynchronously. The initial response has source_deposit_instructions: null. You will receive a virtual_account.created webhook with the deposit instructions when the account becomes active. Use GET /v1/virtual-accounts/{id} to retrieve the full account details including deposit instructions.


Example: Create Fiat Mode Account

Fiat mode accounts hold USD without automatic crypto conversion. Create by omitting the destination field.

curl -X POST https://api.balampay.com/v1/virtual-accounts \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "idempotency-key: va-fiat-$(uuidgen)" \
  -d '{
    "user_id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "US_BANK",
    "bank": "portage",
    "description": "Treasury holding account"
  }'

Response (Fiat Mode)

{
  "id": "550e8400-e29b-41d4-a716-446655440003",
  "status": "activating",
  "type": "US_BANK",
  "bank": "portage",
  "mode": "fiat",
  "destination": null,
  "source_deposit_instructions": null,
  "description": "Treasury holding account",
  "created_at": "2024-01-15T10:30:00Z"
}

Note: Fiat mode accounts have destination: null. Funds received are held in USD and can be disbursed via the payout endpoint.


Balance (Fiat Mode)

Fiat mode accounts maintain a USD balance. Use this endpoint to check available funds before initiating a payout.

GET /v1/virtual-accounts/{id}/balance
curl -X GET https://api.balampay.com/v1/virtual-accounts/550e8400-e29b-41d4-a716-446655440003/balance \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "x-api-key: YOUR_API_KEY"
{
  "virtual_account_id": "550e8400-e29b-41d4-a716-446655440003",
  "available_balance": "5000.00",
  "currency": "USD",
  "updated_at": "2024-01-15T14:30:00Z"
}

Payouts

Both account modes support payouts to send funds to a recipient's bank account via WIRE or SWIFT transfer.

VA ModePayout TypeHow It Works
FiatFiat PayoutDebits the VA balance and initiates a wire/SWIFT transfer
CryptoCrypto PayoutGenerates a single-use deposit wallet; send stablecoins to fund the wire transfer

Preview fees first:

POST /v1/virtual-accounts/{id}/payout/preview

Execute payout:

POST /v1/virtual-accounts/{id}/payout

Tip: Always call the preview endpoint first to display fees to users before they confirm the payout.

For complete payout documentation including request/response formats, fees, status lifecycle, and webhooks, see the Payouts Guide.


Virtual Account Status

StatusDescriptionNext States
pendingAccount creation initiatedactivating, active, failed
activatingAccount is being provisioned asynchronouslyactive, failed
activeAccount is ready to receive depositsdeactivated
failedAccount creation failed (terminal)None
deactivatedAccount has been closed (terminal)None

Status Flow

graph LR
    A[pending] --> B[activating]
    A --> C[active]
    A --> D[failed]
    B --> C[active]
    B --> D[failed]
    C --> E[deactivated]

Get Virtual Account

Endpoint

GET /v1/virtual-accounts/{id}

Example Request

curl -X GET https://api.balampay.com/v1/virtual-accounts/550e8400-e29b-41d4-a716-446655440001 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "x-api-key: YOUR_API_KEY"

Response

{
  "id": "550e8400-e29b-41d4-a716-446655440001",
  "status": "active",
  "type": "US_BANK",
  "bank": "portage",
  "mode": "crypto",
  "destination": {
    "currency": "USDC",
    "network": "solana",
    "address": "7tQJvFk8XZoaVRjLGcBdqN3hJq8VqBvGpPQy8R9xYwZ1"
  },
  "source_deposit_instructions": {
    "currency": "usd",
    "bank_name": "Portage Bank",
    "bank_address": "123 Main St, Portage, MI 49002, US",
    "bank_account_number": "1234567890",
    "bank_routing_number": "021000021",
    "bank_beneficiary_name": "John Doe",
    "bank_beneficiary_address": "123 Main St, New York, NY 10001, US",
    "swift_code": "PORTUS44"
  },
  "description": "Main receiving account",
  "created_at": "2024-01-15T10:30:00Z"
}

Deposit Instructions

The source_deposit_instructions contain the details that users need to receive payments.

US_BANK Instructions

Bank Name: [bank_name]
Bank Address: [bank_address]

Account Number: [bank_account_number]
Routing Number: [bank_routing_number]

Beneficiary Name: [bank_beneficiary_name]
Beneficiary Address: [bank_beneficiary_address]

For ACH transfers:

  • Domestic transfers from any U.S. bank
  • Typically arrive within 1-3 business days
  • Low or no fees for the sender

For Wire transfers:

  • Same-day or next-day transfers
  • Can be domestic or international
  • Higher fees (typically $15-$50)

For Swift transfers:

  • International wire transfers using the swift_code field
  • swift_code is included in source_deposit_instructions when available for the bank
  • Required by the sender's bank to route international payments
  • Typical arrival time: 1-5 business days
  • Higher fees than domestic wires

How Deposits Work

Step-by-Step Flow

sequenceDiagram
    participant Sender
    participant Sender Bank
    participant Kira
    participant Blockchain
    participant User Wallet

    Note over Sender,User Wallet: Transfer Flow

    Sender->>Sender Bank: Initiate transfer to virtual account
    Sender Bank->>Kira: Send USD

    Note over Kira: Funds received

    Note over Kira: Convert to USDC

    Kira->>Blockchain: Send USDC to destination
    Blockchain->>User Wallet: Crypto credited

    Kira->>Your Server: Webhook: Deposit notification

Timing

Transfer TypeArrival TimeConversionTotal Time
ACH1-3 business daysInstant1-3 business days
Wire (Domestic)Same day or next dayInstantSame day - 1 business day
Wire (International)1-5 business daysInstant1-5 business days

Fiat Mode Flow

sequenceDiagram
    participant Sender
    participant Sender Bank
    participant Kira
    participant Your Server
    participant Banking Partner
    participant Recipient Bank

    Note over Sender,Recipient Bank: Deposit Flow

    Sender->>Sender Bank: Initiate transfer to virtual account
    Sender Bank->>Kira: Send USD
    Note over Kira: Funds received
    Note over Kira: Update balance
    Kira->>Your Server: Webhook: deposit_funds_received

    Note over Your Server,Recipient Bank: Payout Flow (when ready)

    Your Server->>Kira: POST /payout (with recipient_id)
    Note over Kira: Validate & calculate fees
    Kira->>Banking Partner: Initiate WIRE/SWIFT transfer
    Banking Partner->>Recipient Bank: Send funds
    Kira->>Your Server: Webhook: payout.completed

Fees

Fees may be deducted from the deposit amount before conversion:

Deposit Received: $1,000.00 USD
Platform Fee (2%): $20.00
Net Amount: $980.00
USDC Sent: ~980 USDC

Fee configuration varies by client. Check with your account manager for details.


Best Practices

General

  1. Validate wallet addresses - Double-check wallet addresses before creating accounts
  2. Select appropriate bank - Choose the bank provider based on your use case
  3. Use descriptive names - Add meaningful descriptions to identify account purposes
  4. Store deposit instructions - Save source_deposit_instructions for display to users
  5. Monitor via webhooks - Use webhooks for real-time status and deposit notifications
  6. Handle async creation - For US_BANK, wait for active status before sharing bank details
  7. Test in sandbox - Verify your integration in sandbox before production

Fiat Mode Specific

  1. Choose bank carefully - Use portage for non-US based users, austin_capital_trust for US-based users. Only slovak_savings_bank is available in sandbox
  2. For payout best practices, see the Payouts Guide

Displaying Instructions to Users

US_BANK Instructions Template

To receive USD payments, share these details with the sender:

Bank Name: {bank_name}
Bank Address: {bank_address}

Account Information:
Account Number: {bank_account_number}
Routing Number: {bank_routing_number}

Beneficiary:
Name: {bank_beneficiary_name}
Address: {bank_beneficiary_address}

Transfer Times:
- ACH transfers: 1-3 business days
- Wire transfers: Same day or next day

Once received, USD will be automatically converted to USDC
and sent to your wallet.

Next Steps

After creating a virtual account:

  1. Track deposits and monitor settlement
  2. Set up webhooks for real-time notifications
  3. Display deposit instructions to your users
  4. Initiate payouts when needed (available for both fiat and crypto mode)

Support