Virtual Accounts
Overview
Virtual accounts are bank accounts that can operate in two modes:
- Crypto Mode: Automatically convert incoming deposits into cryptocurrency and settle funds to a designated blockchain wallet
- 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:
- User has been created
- User has completed identity verification (KYC/KYB)
- User status is
VERIFIED - For Crypto Mode: User has a blockchain wallet (Solana or Polygon)
- For Fiat Mode: No wallet required at creation time
Create Virtual Account
Endpoint
POST /v1/virtual-accounts
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer token from authentication |
x-api-key | Yes | Your API key |
Content-Type | Yes | Must be application/json |
idempotency-key | Yes | Unique 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
| Field | Type | Required | Description |
|---|---|---|---|
user_id | string (UUID) | Yes | The user who will own this virtual account |
type | string | Yes | Account type: US_BANK |
bank | string | Yes | Bank provider: portage or slovak_savings_bank |
destination | object | Conditional | Required for crypto mode. Omit for fiat mode. |
destination.currency | string | Conditional | Cryptocurrency: USDC or USDT (required if destination provided) |
destination.network | string | Conditional | Blockchain: solana or polygon (required if destination provided) |
destination.address | string | Conditional | Wallet address on the specified blockchain (required if destination provided) |
description | string | No | Human-readable description of the account |
Account Modes
| Mode | Destination Field | Response mode | Description |
|---|---|---|---|
| Crypto | Required | "crypto" | Deposits auto-convert to crypto and settle to wallet |
| Fiat | Omit | "fiat" | Deposits held in USD, initiate payouts when needed |
Important: The account mode is determined at creation and cannot be changed later. The
modefield is included in all API responses.
Bank Options
| Bank | Description | Fiat Mode Supported | Use For |
|---|---|---|---|
portage | Portage Bank | Yes | Non-US based users |
austin_capital_trust | Austin Capital Trust | Yes | US-based users |
slovak_savings_bank | Slovak Savings Bank | Yes | Sandbox only |
Deprecated:
lead_bankis no longer supported. Existing accounts remain functional, but new accounts cannot be created with this bank.Sandbox: Only
slovak_savings_bankis available in the sandbox environment.Production: Use
portagefor non-US based users andaustin_capital_trustfor US-based users.
Supported Configurations
| Currency | Network |
|---|---|
| USDC | Solana |
| USDC | Polygon |
| USDT | Solana |
| USDT | Polygon |
| USDT | Tron |
Address Validation
Wallet addresses are validated based on the network:
| Network | Format | Example |
|---|---|---|
| Solana | Base58, 32-44 characters | 7tQJvFk8XZoaVRjLGcBdqN3hJq8VqBvGpPQy8R9xYwZ1 |
| Polygon | 0x + 40 hex characters | 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1 |
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 avirtual_account.createdwebhook with the deposit instructions when the account becomesactive. UseGET /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 Mode | Payout Type | How It Works |
|---|---|---|
| Fiat | Fiat Payout | Debits the VA balance and initiates a wire/SWIFT transfer |
| Crypto | Crypto Payout | Generates 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
| Status | Description | Next States |
|---|---|---|
pending | Account creation initiated | activating, active, failed |
activating | Account is being provisioned asynchronously | active, failed |
active | Account is ready to receive deposits | deactivated |
failed | Account creation failed (terminal) | None |
deactivated | Account 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_codefield swift_codeis included insource_deposit_instructionswhen 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 Type | Arrival Time | Conversion | Total Time |
|---|---|---|---|
| ACH | 1-3 business days | Instant | 1-3 business days |
| Wire (Domestic) | Same day or next day | Instant | Same day - 1 business day |
| Wire (International) | 1-5 business days | Instant | 1-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
- Validate wallet addresses - Double-check wallet addresses before creating accounts
- Select appropriate bank - Choose the bank provider based on your use case
- Use descriptive names - Add meaningful descriptions to identify account purposes
- Store deposit instructions - Save
source_deposit_instructionsfor display to users - Monitor via webhooks - Use webhooks for real-time status and deposit notifications
- Handle async creation - For US_BANK, wait for
activestatus before sharing bank details - Test in sandbox - Verify your integration in sandbox before production
Fiat Mode Specific
- Choose bank carefully - Use
portagefor non-US based users,austin_capital_trustfor US-based users. Onlyslovak_savings_bankis available in sandbox - 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:
- Track deposits and monitor settlement
- Set up webhooks for real-time notifications
- Display deposit instructions to your users
- Initiate payouts when needed (available for both fiat and crypto mode)
Support
- Email: [email protected]
- Deposits Guide: deposits.md
- Payouts Guide: payouts.md
- Webhooks Guide: webhooks.md
-
Updated 21 days ago
