Customer VIBANs

Per-customer virtual IBANs that allow your end-users to receive money in GBP / USD / EUR. Each VIBAN is bound to a single verified customer + currency.

CAD doesn’t use VIBANs — it uses Interac e-Transfer auto-deposit emails. Set the customer’s interac_email at create time + our FX rail routes inbound Interac transfers to your wallet automatically.


The customer-VIBAN object

{
  "object": "virtual_account",
  "id": "ckva_xxxxxxxxxxxx",
  "customer_id": "ckcust_xxxxxxxxxxxx",
  "customer_reference": "pouch-user-12345",
  "currency": "GBP",
  "env": "live",
  "status": "active",
  "account_number": "12345678",
  "account_name": "TECHNEST/POUCH-USER-12345",
  "bank_name": "Clear Junction Limited",
  "bank_code": "200000",
  "provider_ref": "blz_va_xxx",
  "created_at": "2026-01-15T10:00:00Z"
}

Status values: provisioning (awaiting upstream activation webhook) → active (ready to receive) | failed | closed.


Provision a VIBAN

POST /v1/customers/{id}/virtual_accounts

⚠️

Pre-conditions:

  1. Remittances feature flag enabled on your merchant account
  2. API key has virtual_account_create permission (Owner-only by default)
  3. Customer status = verified
  4. Currency must be GBP / USD / EUR
  5. Merchant has an active wallet for that currency in this env

Request

{
  "currency": "GBP"
}

That’s it — Swappr resolves the upstream wallet identifier from your account config + provisions the VIBAN linked to the customer.

Example

curl https://api.swappr.me/v1/customers/ckcust_xxx/virtual_accounts \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "currency": "GBP" }'

Response

201 Created (new VIBAN) or 200 OK (idempotent — VIBAN already exists for this customer + currency).

If account_number is empty in the response, the VIBAN is in provisioning status — the upstream rail hasn’t returned the real account number yet. Subscribe to the virtual_account_activated webhook to be notified when the real account number lands.

Errors

CodeHTTPCause
customer_not_found404Wrong customer id or doesn’t belong to you
customer_not_verified422Customer KYC not approved yet — verify first
customer_not_provisioned422Customer was never successfully created upstream
wallet_not_found422No active wallet for that currency in this env
rail_not_configured503This currency’s VIBAN rail isn’t configured for your account. Contact support.
provider_error502Upstream rail error — retry with same idempotency key

List a customer’s VIBANs

GET /v1/customers/{id}/virtual_accounts

ParamNotes
limit1-100, default 50
starting_afterCursor
statusFilter by status

Returns all VIBANs belonging to the specified customer.

Response

{
  "object": "list",
  "has_more": false,
  "data": [
    { "object": "virtual_account", "id": "...", "currency": "GBP", "status": "active", ... },
    { "object": "virtual_account", "id": "...", "currency": "USD", "status": "active", ... }
  ]
}

Sandbox limitation

Per our FX rail’s documentation:

In the development environment, only NGN virtual bank accounts settle.

GBP / USD / EUR VIBANs can be provisioned in sandbox + show as provisioning in our DB, but the account_number field stays empty because no real bank-rail account number is issued. Real settle validation requires live env.

Use sandbox for end-to-end integration testing of the customer-create + VIBAN-provision flow; switch to live for actual inflow testing.


Inflow webhooks

When money lands at a customer’s VIBAN, Swappr fires a wallet_funded event with a customer block identifying which end-user the inflow belongs to:

{
  "event": "wallet_funded",
  "merchantId": "ckxxxxxxxxxxxxxxxxxx",
  "virtualAccount": {
    "provider": "blaaiz",
    "accountNumber": "12345678",
    "bankName": "Clear Junction Limited",
    "bankCode": "200000"
  },
  "amountMinor": "5000000",
  "currency": "GBP",
  "customer": {
    "id": "ckcustxxxxxxxxxxxxxxxxxx",
    "customer_reference": "your-user-reference"
  },
  "sender": {
    "name": "EMILY HARRIET ASHWORTH",
    "accountNumber": "87654321",
    "sortCode": "203000",
    "bankName": "Barclays UK"
  },
  "narration": "Top-up from Emily",
  "ledgerEntryId": "ckxxxxxxxxxxxxxxxxxx",
  "providerEventId": "ckxxxxxxxxxxxxxxxxxx",
  "receivedAt": "2026-05-05T12:34:56.789Z"
}

Use customer.customer_reference to attribute the inflow back to the right user in your system. The merchant wallet (your business wallet) is credited; per-user accounting on your side is your responsibility.

For the CAD-Interac rail, the sender block uses interac_email instead of bank-rail fields:

"sender": {
  "interac_email": "demo.sender@example.ca",
  "name": "MARGARET CHANTAL DUBOIS"
}

Same wallet_funded event name — branch on event.virtualAccount.provider === 'blaaiz' plus the presence of sender.interac_email to detect Interac flows.