Virtual Wallets API
Virtual Wallets enable your customers to hold and receive funds in multiple currencies. This API allows you to onboard customers to virtual wallets, which creates dedicated virtual accounts for receiving deposits.
Overview
Virtual wallets provide:
- Virtual Account Numbers - Dedicated account numbers for receiving bank transfers
- Multi-Currency Support - Support for CAD, USD, EUR, and GBP
- Automated Deposits - Incoming transfers are automatically credited to the customer's wallet
- KYC Integration - Leverages existing KYC verification for seamless onboarding
Virtual Wallet Onboarding
Onboard a customer to a virtual wallet for a specific currency. This endpoint initiates the onboarding process for the specified currency.
Prerequisites
- Customer must have completed KYC verification
- Customer must be registered and linked to your business account
- Valid API key with appropriate permissions
Endpoint
POST /api/v1/client/virtual-wallets
Authentication
This endpoint requires HMAC authentication (Business API Key). Include your API key and signature in the request headers:
x-api-key: <your-api-key>
x-timestamp: <RFC3339-timestamp>
x-signature: <HMAC-signature>For detailed information on HMAC authentication, see the Authentication Guide.
Request Body
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
user_id | string (UUID) | Yes | Customer's user ID (must be a valid UUID) | "123e4567-e89b-12d3-a456-426614174000" |
currency | string | Yes | Currency code for virtual wallet onboarding | "CAD" |
Supported Currencies
| Currency Code | Currency Name |
|---|---|
CAD | Canadian Dollar |
USD | US Dollar |
EUR | Euro |
GBP | British Pound |
Request Example
{
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"currency": "CAD"
}Success Response (200 OK)
{
"message": "Customer onboarding completed successfully",
"currency": "CAD",
"user_id": "123e4567-e89b-12d3-a456-426614174000"
}Response Fields
| Field | Type | Description |
|---|---|---|
message | string | Success message indicating onboarding completion |
currency | string | Currency code that was onboarded |
user_id | string (UUID) | Customer user ID that was onboarded |
Error Responses
| Status Code | Description |
|---|---|
400 | Invalid request format, missing required fields, invalid UUID, or invalid currency |
401 | Unauthorized - Invalid API key or signature |
403 | User has not completed KYC verification |
404 | No KYC record found for the provided user ID |
500 | Internal server error during onboarding process |
Error Response Examples
User has not completed KYC (403):
{
"error": "user is yet to complete kyc"
}No KYC record found (404):
{
"error": "no kyc record for id provided"
}Invalid currency (400):
{
"error": "invalid currency"
}Example: cURL
curl -X POST https://api.sznd.app/api/v1/client/virtual-wallets \
-H "Content-Type: application/json" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T12:20:15Z" \
-H "x-signature: generated_signature_here" \
-d '{
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"currency": "CAD"
}'Example: JavaScript
const crypto = require('crypto');
async function onboardVirtualWallet(userId, currency) {
const apiKey = 'your_api_key_here';
const secretKey = 'your_secret_key_here';
const timestamp = new Date().toISOString();
const body = JSON.stringify({
user_id: userId,
currency: currency
});
// Generate HMAC signature
const dataToSign = `${body}|${timestamp}`;
const signature = crypto
.createHmac('sha256', secretKey)
.update(dataToSign)
.digest('hex');
const response = await fetch('https://api.sznd.app/api/v1/client/virtual-wallets', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'x-timestamp': timestamp,
'x-signature': signature
},
body: body
});
return await response.json();
}
// Usage
const result = await onboardVirtualWallet(
'123e4567-e89b-12d3-a456-426614174000',
'CAD'
);
console.log(result);List Virtual Wallets
List virtual wallet (VBA) details for your business. Returns one item per virtual bank account. Supports optional filters and pagination. Only VBAs associated with your business are returned.
Processing Time & Availability
After submitting a virtual wallet onboarding request via POST /api/v1/client/virtual-wallets:
- Processing Time: The virtual wallet setup is processed asynchronously and typically completes within 24 hours
- Webhook Notification: A webhook will be delivered to your configured webhook URL when the virtual wallet is ready
- Polling: You can list virtual wallets after 24 hours to see new entries, or wait for the webhook notification
Endpoint
GET /api/v1/client/virtual-wallets
Authentication
This endpoint requires HMAC authentication (Business API Key). Include your API key and signature in the request headers:
x-api-key: <your-api-key>
x-timestamp: <RFC3339-timestamp>
x-signature: <HMAC-signature>For detailed information on HMAC authentication, see the Authentication Guide.
Query Parameters
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
user_id | string (UUID) | No | Filter by customer user ID | "123e4567-e89b-12d3-a456-426614174000" |
currency | string | No | Filter by currency. One of: CAD, USD, EUR, GBP | "CAD" |
created_between_from | string (RFC 3339) | No | Start of created-at range (requires tz if set) | "2025-01-01T00:00:00Z" |
created_between_to | string (RFC 3339) | No | End of created-at range (requires tz if set) | "2025-01-31T23:59:59Z" |
tz | string (IANA) | Conditional | Client timezone (e.g. America/Toronto). Required when created_between_from or created_between_to is set | "America/Toronto" |
limit | int | No | Page size (default 50, max 500) | 50 |
page | int | No | Page number, 1-based (default 1) | 1 |
Success Response (200 OK)
Returns an object with data (array of virtual wallet detail objects, one per VBA) and pagination (total count and page info):
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"primary_wallet_id": "550e8400-e29b-41d4-a716-446655440000",
"business_id": "biz-uuid-here",
"is_business_primary_wallet": true,
"currency": "CAD",
"account_number": "1234567890",
"account_name": "John Doe",
"bank_name": "Example Bank",
"bank_code": "001",
"sort_code": "",
"bic_or_swift_code": "",
"routing_number": "",
"iban": "",
"email": "customer@example.com",
"question": "",
"answer": "",
"country_code": "CA",
"bank_address": "",
"bank_beneficiary_address": "",
"is_primary": true,
"is_active": true,
"created_at": "2025-01-30T10:00:00Z",
"updated_at": "2025-01-30T10:00:00Z"
}
],
"pagination": {
"total": 1,
"page": 1,
"size": 50,
"total_pages": 1
}
}Pagination
| Field | Type | Description |
|---|---|---|
total | integer | Total number of VBAs matching the filters (across all pages) |
page | integer | Current page number (1-based) |
size | integer | Page size (number of items in this response) |
total_pages | integer | Total number of pages for the current filters |
Response Fields (each item in data)
| Field | Type | Description |
|---|---|---|
id | string (UUID) | VBA record ID |
user_id | string (UUID) | Customer's user ID |
primary_wallet_id | string (UUID) | Wallet this VBA is linked to |
business_id | string (UUID) | Business ID (when pooled) |
is_business_primary_wallet | boolean | Whether funds go to business pool wallet |
currency | string | Currency code (CAD, USD, EUR, GBP) |
account_number | string | Virtual account number for receiving deposits |
account_name | string | Account holder name |
bank_name | string | Name of the bank |
bank_code | string | Bank code (if applicable) |
sort_code | string | Sort code (GBP) |
bic_or_swift_code | string | BIC/SWIFT code |
routing_number | string | Routing number (USD) |
iban | string | IBAN (if applicable) |
email | string | Email associated with the account |
country_code | string | Country code |
bank_address | string | Bank address |
bank_beneficiary_address | string | Beneficiary address |
is_primary | boolean | Whether this is the primary VBA for the wallet |
is_active | boolean | Whether the VBA is active |
created_at | string (ISO 8601) | When the VBA was created |
updated_at | string (ISO 8601) | When the VBA was last updated |
Error Responses
| Status Code | Description |
|---|---|
400 | Invalid query (e.g. invalid UUID, invalid currency, date filter without tz, invalid time zone) |
401 | Unauthorized - Invalid API key or signature |
500 | Internal server error |
Example: cURL (list all, then filter)
# List all virtual wallets for your business (paginated)
curl -X GET "https://api.sznd.app/api/v1/client/virtual-wallets?limit=50&page=1" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T12:20:15Z" \
-H "x-signature: generated_signature_here"
# Filter by customer and currency
curl -X GET "https://api.sznd.app/api/v1/client/virtual-wallets?user_id=123e4567-e89b-12d3-a456-426614174000¤cy=CAD" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T12:20:15Z" \
-H "x-signature: generated_signature_here"Example: JavaScript (list)
const crypto = require('crypto');
async function listVirtualWallets(queryParams = '') {
const apiKey = 'your_api_key_here';
const secretKey = 'your_secret_key_here';
const timestamp = new Date().toISOString();
const url = `https://api.sznd.app/api/v1/client/virtual-wallets${queryParams ? '?' + queryParams : ''}`;
const dataToSign = '' + '|' + timestamp; // GET: empty body
const signature = crypto.createHmac('sha256', secretKey).update(dataToSign).digest('hex');
const response = await fetch(url, {
method: 'GET',
headers: { 'x-api-key': apiKey, 'x-timestamp': timestamp, 'x-signature': signature }
});
return await response.json();
}
const resp = await listVirtualWallets('user_id=123e4567-e89b-12d3-a456-426614174000¤cy=CAD');
console.log('Virtual wallets:', resp.data);
console.log('Total:', resp.pagination.total, 'Page:', resp.pagination.page, 'of', resp.pagination.total_pages);Get Virtual Wallet by ID
Retrieve all virtual bank account (VBA) details for a single wallet by wallet ID. The customer that owns the wallet must belong to your business.
Endpoint
GET /api/v1/client/virtual-wallets/:id
| Part | Description |
|---|---|
id | Wallet ID (UUID). Path parameter. |
Authentication
Same as List: HMAC (Business API Key). See Authentication Guide.
Success Response (200 OK)
Returns an array of virtual wallet detail objects (same shape as each item in the List response). One entry per VBA linked to that wallet. If the wallet has no VBAs, the array is empty.
[
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"primary_wallet_id": "550e8400-e29b-41d4-a716-446655440000",
"business_id": "biz-uuid-here",
"is_business_primary_wallet": true,
"currency": "CAD",
"account_number": "1234567890",
"account_name": "John Doe",
"bank_name": "Example Bank",
"bank_code": "001",
"sort_code": "",
"bic_or_swift_code": "",
"routing_number": "",
"iban": "",
"email": "customer@example.com",
"question": "What is the answer",
"answer": "0234568ZZ",
"country_code": "CA",
"bank_address": "",
"bank_beneficiary_address": "",
"is_primary": true,
"is_active": true,
"created_at": "2025-01-30T10:00:00Z",
"updated_at": "2025-01-30T10:00:00Z"
}
]Error Responses
| Status Code | Description |
|---|---|
400 | Invalid wallet ID (not a valid UUID) |
401 | Unauthorized - Invalid API key or signature |
403 | Customer does not belong to your business |
404 | Virtual wallet not found (wallet ID unknown or user has no BVA config for that wallet) |
500 | Internal server error |
Example: cURL
curl -X GET "https://api.sznd.app/api/v1/client/virtual-wallets/550e8400-e29b-41d4-a716-446655440000" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T12:20:15Z" \
-H "x-signature: generated_signature_here"Example: JavaScript
async function getVirtualWalletById(walletId) {
const apiKey = 'your_api_key_here';
const secretKey = 'your_secret_key_here';
const timestamp = new Date().toISOString();
const dataToSign = '' + '|' + timestamp;
const signature = crypto.createHmac('sha256', secretKey).update(dataToSign).digest('hex');
const response = await fetch(
`https://api.sznd.app/api/v1/client/virtual-wallets/${walletId}`,
{
method: 'GET',
headers: { 'x-api-key': apiKey, 'x-timestamp': timestamp, 'x-signature': signature }
}
);
return await response.json();
}
const vbaList = await getVirtualWalletById('550e8400-e29b-41d4-a716-446655440000');
console.log('VBAs for wallet:', vbaList);Webhook Notification
When the virtual wallet is ready, a webhook will be sent to your configured webhook URL. The webhook payload will include:
- Event type:
virtual_wallet.ready - User ID and currency
- Virtual wallet ID
- Bank account details
- Timestamp
Configure your webhook URL in your business settings to receive these notifications automatically. See the Webhooks Documentation for more details.
Integration Flow
The complete virtual wallet integration follows these steps:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Virtual Wallet Onboarding Flow β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β 1. Register Customer β
β POST /api/v1/client/customers β
β ββ> Returns user_id β
β β
β 2. Complete KYC Verification β
β POST /api/v1/client/customers/kyc β
β ββ> Customer completes KYC verification β
β β
β 3. Onboard to Virtual Wallet β
β POST /api/v1/client/virtual-wallets β
β ββ> Initiates virtual wallet creation process β
β β
β 4. Wait for Processing (24 hours) β
β ββ> Virtual wallet setup is processed asynchronously β
β ββ> Webhook notification sent when ready β
β β
β 5. List or get virtual wallet details β
β GET /api/v1/client/virtual-wallets (list, optional filters) β
β GET /api/v1/client/virtual-wallets/{wallet_id} (by wallet) β
β ββ> Returns array of VBA details (one item per virtual account) β
β β
β 6. Customer Receives Virtual Account β
β ββ> Virtual account number available β
β ββ> Customer can receive deposits via bank transfer β
β β
β 7. Funds Available in Wallet β
β ββ> Incoming transfers credited automatically β
β ββ> Check balance via GET /api/v1/users/{id}/wallets β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββStep 1: Register Customer
First, register the customer using the Customer Registration endpoint:
curl -X POST https://api.sznd.app/api/v1/client/customers \
-H "Content-Type: application/json" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T12:20:15Z" \
-H "x-signature: generated_signature_here" \
-d '{
"email": "customer@example.com",
"first_name": "John",
"last_name": "Doe",
"phone_number": "+1234567890",
"country_code": "CA",
"date_of_birth": "1990-05-15"
}'Step 2: Complete KYC
The customer must complete KYC verification. See the KYC Documentation for details.
Step 3: Onboard to Virtual Wallet
Once KYC is complete, onboard the customer to the virtual wallet:
curl -X POST https://api.sznd.app/api/v1/client/virtual-wallets \
-H "Content-Type: application/json" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T12:20:15Z" \
-H "x-signature: generated_signature_here" \
-d '{
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"currency": "CAD"
}'Step 4: Wait for Processing
The virtual wallet setup is processed asynchronously and typically completes within 24 hours. You have two options:
Option A: Wait for Webhook Notification (Recommended)
Configure your webhook URL to receive a notification when the virtual wallet is ready. The webhook will include all bank account details.
Option B: Poll the list or get-by-id endpoint
After 24 hours, list virtual wallets (optionally filter by user_id, currency) or get by wallet ID:
# List all VBAs for your business (optional: ?user_id=...¤cy=...)
curl -X GET "https://api.sznd.app/api/v1/client/virtual-wallets" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T12:20:15Z" \
-H "x-signature: generated_signature_here"
# Or get VBAs for a specific wallet by ID
curl -X GET "https://api.sznd.app/api/v1/client/virtual-wallets/550e8400-e29b-41d4-a716-446655440000" \
-H "x-api-key: your_api_key_here" \
-H "x-timestamp: 2025-01-30T12:20:15Z" \
-H "x-signature: generated_signature_here"Step 5: Use Virtual Account Details
Once the virtual wallet is ready, use the bank account details from the list or get-by-id response (each item has account_number, bank_name, iban, etc.) to receive deposits. The customer can receive funds via bank transfer using the provided account information.
Important Notes
KYC Requirement
The customer must have completed KYC verification before virtual wallet onboarding can proceed. The system checks the KYC status and will return a 403 error if KYC is not completed.
Idempotency
If a customer is already onboarded for a specific currency, the endpoint may return success if the wallet already exists.
Processing Time
The virtual wallet onboarding request is processed asynchronously. After submitting the POST request:
- Processing Time: Virtual wallet setup typically completes within 24 hours
- Webhook Notification: A webhook will be delivered to your configured webhook URL when the virtual wallet is ready
- Retrieving Details: After 24 hours, you can list virtual wallets with
GET /api/v1/client/virtual-wallets(optional filters:user_id,currency, pagination) or get by wallet ID withGET /api/v1/client/virtual-wallets/{id}to retrieve bank account details, or wait for the webhook notification
Virtual Account Details
After successful onboarding, the customer's wallet will include virtual account details (account number, bank name, etc.) that can be used to receive deposits. These details are available through the wallet API endpoints.
Related Resources
- Virtual Wallets API - View virtual wallet balances and details
- Customers API - Customer registration and management
- KYC Documentation - KYC verification process
- Transactions API - View transaction history