Copy of Configure virtual accounts
Implementation of virtual account APIs to facilitate payment routing for submerchants, featuring individual virtual accounts with multiple aliases for redundancy.
We are implementing virtual account APIs to support payment routing for submerchants.
The key capabilities include:
- Virtual accounts - Individual accounts for each submerchant with multiple aliases for redundancy
- Multi-level redundancy - Multiple aliases (IBANs) per virtual account across providers and countries
- Payment routing - Route payments directly to specific virtual accounts
What are virtual accounts?
Virtual accounts are accounts with an account holder name and multiple aliases (IBANs) associated with a merchant account that can receive payments. Virtual accounts share the same balance of the merchant account.
Each virtual account is automatically created with multiple aliases. These are individual IBANs from different banking providers and countries that all route to the same virtual account. This provides built-in redundancy at both the provider and local IBAN level without requiring you to manage multiple accounts per sub-merchant.
Virtual account aliases provide multi-level redundancy to support Stripe's sub-merchant requirements:
- Provider redundancy: Multiple aliases across different banking providers (eg LHV, Clearbank, Banking Circle) reduce single-provider risk
- Local IBAN redundancy: Multiple IBANs within the same country protect against individual IBAN blocking or issues
- Geographic distribution: Aliases across multiple countries provide broader operational resilience
- Automatic failover: If one provider or specific IBAN experiences issues, payments route through alternative aliases
- Simplified management: One virtual account per submerchant, with automatic multi-level redundancy provisioning
Virtual Account Lifecycle
Virtual accounts progress through different states during their lifecycle:
Status States:
pending- Virtual account is being created asynchronouslyenabled- Virtual account is active and available for payment routingdisabled- Virtual account is inactive and will not be used for new paymentsfailed- Virtual account creation has failed
State Transitions:
- New virtual accounts start in
pendingstatus during creation - Upon successful creation, status transitions to
enabled - If creation fails, status becomes
failed - Enabled virtual accounts can be transitioned to
disabledvia the disable API - Important: Primary virtual accounts (marked with
is_primary: true) cannot be disabled
How will we implement it?
1. Distribute IBAN risk rollout
In the first phase, TrueLayer will set up a limited number of additional virtual accounts for you. This reduces the risk of a single IBAN blocking affecting payment routing.
You will initiate a payment with an indication of the Virtual Account to be used.
In this phase, we do not support multiple aliases for a Virtual Account. Therefore, each virtual account will have only one alias (IBAN).
Model:
Note: Virtual Account A and B are pre-configured by TrueLayer for limited rollout testing. Each virtual account has one alias.
Available Endpoints:
GET /v3/merchant-accounts/{id}/virtual-accounts- List pre-configured virtual accountsGET /v3/merchant-accounts/{id}/virtual-accounts/{id}/aliases- List aliases for each virtual accountPOST /v3/payments- Create payments with direct virtual account routingGET /v3/payments/{payment_id}- Retrieve payment details
Note: Virtual account creation is not available in Phase 1 - only pre-configured accounts can be used.
Payment Processing:
To control which virtual account a payment goes to, include a routing_configuration object inside the beneficiary object when you call the POST /v3/payments endpoint. Set the type to virtual_account and include the virtual_account_id.
See the example below:
# Route payments to specific sub-merchant virtual accounts
POST /v3/payments
{
"amount_in_minor": 10000,
"currency": "GBP",
"payment_method": {
"beneficiary": {
"type": "merchant_account",
"routing_configuration": {
"type": "virtual_account",
"virtual_account_id": "{virtual_account_id}"
}
}
}
}You can also call GET /v3/merchant-accounts/{id}/virtual-accounts to see a list of pre-configured virtual accounts, and GET /v3/merchant-accounts/{id}/virtual-accounts/{id}/aliases to see a list of aliases for each virtual account.
2. Self-service virtual account creation
In this phase, you create virtual accounts for thousands of sub-merchants with full API access, creating one virtual account per sub-merchant.
Each virtual account is automatically provisioned with a single alias during this phase because smart routing across aliases is not supported.
Model:
Additional Endpoints:
POST /v3/merchant-accounts/{id}/virtual-accounts- Create virtual accounts with sub-merchant detailsPOST /v3/merchant-accounts/{id}/virtual-accounts/{virtual_account_id}/disable- Disable virtual accounts
Enhanced Endpoints:
GET /v3/merchant-accounts/{merchant_account_id}/transactions- Get transactions with virtual account id indication.
Webhook Events:
virtual_account_created- Account creation successvirtual_account_creation_failed- Account creation failure.
Sub-merchant Onboarding
To create a virtual account, call the POST /v3/merchant-accounts/{id}/virtual-accounts endpoint
# Create virtual account with full KYC details for sub-merchant
POST /v3/merchant-accounts/{id}/virtual-accounts
{
"account_holder_name": "Etsy",
"sub_merchants": {
"ultimate_counterparty": {
"type": "business_client",
"actor": {
"name": "John Smith",
"date_of_birth": "1985-03-15",
"country_code": "GB"
},
"company": {
"registration_number": "12345678",
"registration_address": {
"line_1": "25 Tech Street",
"city": "Manchester",
"postal_code": "M1 1AA",
"country_code": "GB"
}
}
}
}
}
# → Returns virtual_account_id and triggers async creation with a single aliasCreate virtual account webhooks
If your virtual account is created successfully, you receive a virtual_account_created webhook.
{
"type": "virtual_account_created",
"event_version": 1,
"virtual_account_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"account_holder_name": "Etsy",
"account_identifiers": [
{
"type": "iban",
"iban": "GB33BUKB20201555555555"
}
],
"event_id": "evt_123456789",
"timestamp": "2023-11-27T10:35:00Z"
}If your virtual account is not created, you receive a virtual_account_creation_failed webhook.
{
"type": "virtual_account_creation_failed",
"event_version": 1,
"error": {
"type": "validation_error",
"message": "Invalid company registration number"
},
"event_id": "evt_123456790",
"timestamp": "2023-11-27T10:35:00Z"
}3. Advanced routing
Enhanced virtual account management with intelligent alias selection and routing optimization.
Model:
Key Features:
- Alias redundancy across local IBAN country and providers
- Smart routing selection of alias
- Transparent Enhancement - existing virtual accounts will be enriched with multiple aliases transparently to Stripe, as TrueLayer handles this automatically
- No integration changes required for Stripe - existing virtual account routing continues to work seamlessly
New API Endpoints
1. Virtual Account Management
List Virtual Accounts (Available in Phase 1)
GET /v3/merchant-accounts/{merchant_account_id}/virtual-accountsPurpose: Retrieve all virtual accounts for discovery and payment routing configuration.
Response:
{
"items": [
{
"id": "83a9c953-2d33-431c-978c-fad1856c4e9f",
"account_holder_name": "Stripe",
"status": "enabled",
"is_primary": true
},
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"account_holder_name": "Stripe",
"status": "enabled",
"is_primary": false
},
{
"id": "6ba7b810-9b89-4dad-a9d3-4f3e5c430c8e",
"account_holder_name": "Stripe",
"status": "enabled",
"is_primary": false
}
],
"pagination": {
"has_more": false,
"next_cursor": null
}
}Get Virtual Account Aliases (Available in Phase 1)
GET /v3/merchant-accounts/{merchant_account_id}/virtual-accounts/{virtual_account_id}/aliasesPurpose: Retrieve all aliases (IBANs) for a specific virtual account, showing provider and local redundancy.
Response:
{
"items": [
{
"id": "alias_83a9c953-2d33-431c-978c-fad1856c4e9f",
"account_identifiers": [
{
"type": "iban",
"iban": "FR76BARC20041234567890"
}
],
"is_default": true
}
],
"pagination": {
"has_more": false,
"next_cursor": null
}
}Note: Each virtual account has one alias during Phase 1 and Phase 2 for operational simplicity.
2. Virtual Account Creation
Create Virtual Account with Sub-merchant Details (Available in Phase 2)
POST /v3/merchant-accounts/{merchant_account_id}/virtual-accountsPurpose: Create virtual accounts with full submerchant information. Virtual accounts are automatically provisioned with a single alias during Phase 2 for operational simplicity.
Request:
{
"account_holder_name": "Etsy",
"sub_merchants": {
"ultimate_counterparty": {
"type": "business_client",
"actor": {
"name": "John Smith",
"date_of_birth": "1985-03-15",
"country_code": "GB"
},
"company": {
"registration_number": "12345678",
"registration_address": {
"line_1": "25 Tech Street",
"city": "Manchester",
"postal_code": "M1 1AA",
"country_code": "GB"
}
}
}
}
}Response (202 Accepted):
{
"virtual_account_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "pending",
"message": "Virtual account creation initiated. Completion will be notified via webhook."
}Note that the virtual account is created async, so a 202 response from this endpoint does not guarantee that a virtual account will be created.
3. Disable Virtual Account
Disable Virtual Account (Available in Phase 2)
POST /v3/merchant-accounts/{merchant_account_id}/virtual-accounts/{virtual_account_id}/disablePurpose: Disable a virtual account to prevent it from being used for payment routing. This is a synchronous operation that immediately updates the virtual account status.
Important Restrictions:
- Primary virtual accounts cannot be disabled and will return a 400 error if attempted
- Once disabled, a virtual account cannot be re-enabled via API
Request:
POST /v3/merchant-accounts/{id}/virtual-accounts/f47ac10b-58cc-4372-a567-0e02b2c3d479/disableNo request body is required.
Response (200 OK):
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}Enhanced Payment APIs
Payment Creation with Virtual Account Routing (Available in Phase 1)
Modified Payment Request
POST /v3/paymentsEnhanced Features:
- Routing Configuration: Route payments directly to specific virtual accounts
Request:
{
"amount_in_minor": 10000,
"currency": "GBP",
"payment_method": {
"type": "bank_transfer",
"provider_selection": {
"type": "user_selected"
},
"beneficiary": {
"type": "merchant_account",
"merchant_account_id": "b1d2e3f4-5678-9012-3456-789abcdef012",
"routing_configuration": {
"type": "virtual_account",
"virtual_account_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
},
"account_holder_name": "Etsy",
"reference": "payment reference"
}
}
}Response:
{
"id": "c2a3f4e5-6789-0123-4567-89abcdef0123",
"amount_in_minor": 10000,
"currency": "GBP",
"status": "authorization_required",
"payment_method": {
"type": "bank_transfer",
"beneficiary": {
"type": "merchant_account",
"merchant_account_id": "b1d2e3f4-5678-9012-3456-789abcdef012",
"routing_configuration": {
"type": "virtual_account",
"virtual_account_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
},
"account_holder_name": "Etsy Ltd"
}
}
}Get Payment Details (Available in Phase 1)
GET /v3/payments/{payment_id}Purpose: Retrieve payment details including resolved virtual account routing information.
Response:
{
"id": "c2a3f4e5-6789-0123-4567-89abcdef0123",
"amount_in_minor": 10000,
"currency": "GBP",
"status": "executed",
"payment_method": {
"type": "bank_transfer",
"beneficiary": {
"type": "merchant_account",
"merchant_account_id": "b1d2e3f4-5678-9012-3456-789abcdef012",
"routing_configuration": {
"type": "virtual_account",
"virtual_account_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
},
"account_holder_name": "Etsy",
"reference": "Payment for TechCorp services"
}
},
"user": {
"name": "Customer Name",
"email": "[email protected]"
},
"created_at": "2023-11-27T10:40:00Z",
"executed_at": "2023-11-27T10:42:00Z"
}Transaction History (Available in Phase 2)
POST /v3/merchant-accounts/{id}/transactionsPurpose: Retrieve transaction history with virtual account information for reconciliation and reporting
Response
{
"items": [
{
"type": "merchant_account_payment",
"id": "tx_abc123",
"currency": "GBP",
"amount_in_minor": 10000,
"status": "settled",
"settled_at": "2023-11-27T10:42:00Z",
"payment_source": {
"id": "ps_def456",
"account_holder_name": "Customer Name",
"account_identifiers": [
{
"type": "iban",
"iban": "GB29NWBK60161331926819"
}
]
},
"payment_id": "c2a3f4e5-6789-0123-4567-89abcdef0123",
"virtual_account_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
],
"pagination": {
"has_more": false,
"next_cursor": null
}
}Note: The virtual_account_id field identifies which virtual account received each payment, enabling sub-merchant reconciliation and reporting.
Webhook Events
Virtual Account Created (Available in Phase 2)
{
"type": "virtual_account_created",
"event_version": 1,
"virtual_account_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"account_holder_name": "Etsy",
"event_id": "evt_123456789",
"timestamp": "2023-11-27T10:35:00Z"
}Virtual Account Creation Failed (Available in Phase 2)
{
"type": "virtual_account_creation_failed",
"event_version": 1,
"error": {
"type": "validation_error",
"message": "Invalid company registration number"
},
"event_id": "evt_123456790",
"timestamp": "2023-11-27T10:35:00Z"
}Updated about 2 hours ago
