πŸ“š Resources
πŸ’Ό Virtual Wallets

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

FieldTypeRequiredDescriptionExample
user_idstring (UUID)YesCustomer's user ID (must be a valid UUID)"123e4567-e89b-12d3-a456-426614174000"
currencystringYesCurrency code for virtual wallet onboarding"CAD"

Supported Currencies

Currency CodeCurrency Name
CADCanadian Dollar
USDUS Dollar
EUREuro
GBPBritish 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

FieldTypeDescription
messagestringSuccess message indicating onboarding completion
currencystringCurrency code that was onboarded
user_idstring (UUID)Customer user ID that was onboarded

Error Responses

Status CodeDescription
400Invalid request format, missing required fields, invalid UUID, or invalid currency
401Unauthorized - Invalid API key or signature
403User has not completed KYC verification
404No KYC record found for the provided user ID
500Internal 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

ParameterTypeRequiredDescriptionExample
user_idstring (UUID)NoFilter by customer user ID"123e4567-e89b-12d3-a456-426614174000"
currencystringNoFilter by currency. One of: CAD, USD, EUR, GBP"CAD"
created_between_fromstring (RFC 3339)NoStart of created-at range (requires tz if set)"2025-01-01T00:00:00Z"
created_between_tostring (RFC 3339)NoEnd of created-at range (requires tz if set)"2025-01-31T23:59:59Z"
tzstring (IANA)ConditionalClient timezone (e.g. America/Toronto). Required when created_between_from or created_between_to is set"America/Toronto"
limitintNoPage size (default 50, max 500)50
pageintNoPage 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

FieldTypeDescription
totalintegerTotal number of VBAs matching the filters (across all pages)
pageintegerCurrent page number (1-based)
sizeintegerPage size (number of items in this response)
total_pagesintegerTotal number of pages for the current filters

Response Fields (each item in data)

FieldTypeDescription
idstring (UUID)VBA record ID
user_idstring (UUID)Customer's user ID
primary_wallet_idstring (UUID)Wallet this VBA is linked to
business_idstring (UUID)Business ID (when pooled)
is_business_primary_walletbooleanWhether funds go to business pool wallet
currencystringCurrency code (CAD, USD, EUR, GBP)
account_numberstringVirtual account number for receiving deposits
account_namestringAccount holder name
bank_namestringName of the bank
bank_codestringBank code (if applicable)
sort_codestringSort code (GBP)
bic_or_swift_codestringBIC/SWIFT code
routing_numberstringRouting number (USD)
ibanstringIBAN (if applicable)
emailstringEmail associated with the account
country_codestringCountry code
bank_addressstringBank address
bank_beneficiary_addressstringBeneficiary address
is_primarybooleanWhether this is the primary VBA for the wallet
is_activebooleanWhether the VBA is active
created_atstring (ISO 8601)When the VBA was created
updated_atstring (ISO 8601)When the VBA was last updated

Error Responses

Status CodeDescription
400Invalid query (e.g. invalid UUID, invalid currency, date filter without tz, invalid time zone)
401Unauthorized - Invalid API key or signature
500Internal 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&currency=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&currency=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

PartDescription
idWallet 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 CodeDescription
400Invalid wallet ID (not a valid UUID)
401Unauthorized - Invalid API key or signature
403Customer does not belong to your business
404Virtual wallet not found (wallet ID unknown or user has no BVA config for that wallet)
500Internal 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=...&currency=...)
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 with GET /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