# BVNK API Documentation > Complete API documentation for BVNK payment infrastructure—crypto payments, wallets, and financial services This file contains all documentation content in a single document following the llmstxt.org standard. ## API Schema Embed Demo This page demonstrates how to embed API endpoint schemas and examples directly in user guides using the `` and `` components. --- ## Request Body Schema Just pass `endpointId` — the title, method, and path are resolved automatically from the OpenAPI spec: --- ## Response Schema (specific status codes) Show only the 201 response: --- ## Request Example Show the curated request example as a code block: --- ## Response Example --- ## URL Parameters --- ## Starts Open Pass `collapsed={false}` to show expanded by default: --- ## Override Auto-resolved Metadata You can still override any field manually: --- ## Usage Reference ### `` | Prop | Type | Description | |------|------|-------------| | `type` | `"request" \| "response" \| "params"` | Which section to embed | | `data` | `object` | Sidecar JSON via `require("@site/docs/bvnk/api-explorer/endpoints/..json")` | | `endpointId` | `string?` | Endpoint ID (filename prefix). Auto-populates name, method, path. | | `endpoint` | `string?` | Override endpoint name | | `method` | `string?` | Override HTTP method | | `path` | `string?` | Override endpoint path | | `title` | `string?` | Override section title | | `collapsed` | `boolean?` | Start collapsed (default: `true`) | | `statusCodes` | `string[]?` | Filter response codes (for `type="response"`) | ### `` | Prop | Type | Description | |------|------|-------------| | `type` | `"request" \| "response"` | Which side's examples to show | | `data` | `object` | Sidecar JSON via `require()` | | `statusCode` | `string?` | For responses, which status code (default: `"200"`) | | `exampleName` | `string?` | Show only this named example | | `title` | `string?` | Title above the code block | --- ## Charge customer fees As a partner, you can add a markup fee to your customers' transactions, enabling you to collect additional revenue from your customers. These customer fees (hereinafter, _customer fees_) are optional charges. They are separate from the transaction amount and transaction fees and are collected from your customer's wallet. ## Implement customer fees To add a customer fee to a transaction, include the `customerFee` object in your API request: ```json Example Payload with customer Fee { "type": "OUT", "fees": { "customerFee": { "currency": "USD", "amount": 1 } } } ``` The example use case for charging the customer fees may look as follows: 1. Send the [`GET /ledger/v2/wallets`](/bvnk/api-explorer/endpoints/ledger-wallet-list-v-2/) request to acquire the list of your existing wallets with details. In the response, find a wallet to which you plan to deposit fees and copy its `id` value. The selected wallet's currency should match the currency of the fees you are charging. You may use both fiat and crypto wallets, configuring an existing wallet or creating a new one for fees. 2. Designate the selected wallet as the fee destination by sending the [`PUT /platform/v1/fees/customer-fee-wallets`](../../api-explorer/endpoints/set-customer-fee-wallet) request. Include the following payload: Here, `destinationWalletId` is the `id` from step 1. 3. Check that your selected wallet can receive customer fees by sending the [`GET /platform/v1/fees/customer-fee-wallets`](../../api-explorer/endpoints/get-customer-fee-wallets) request. In the response, you receive the list of wallets that will be used for accepting customer fees. The wallet with the `id` specified earlier must be listed here. Now, your wallet is configured for receiving customer fees. 4. To apply the fee, when creating [payment requests](../../api-explorer/endpoints/payment-create), include the customer fee information in the `fees` object: ```json Example Payload with customer Fee for Crypto { "walletId": "a:25022613287255:zmHs0pg:1", "amount": 100, "currency": "USDC", "reference": "REF182597", "type": "OUT", "fees": { "customerFee": { "currency": "USDC", "amount": 1 } } } ``` ```json Example Payload with customer Fee for Fiat { "walletId": "a:24071743000626:go5SB1l:1", "amount": 1000, "currency": "USD", "reference": "REF182597", "method": "ACH", "fees": { "customerFee": { "amount": 0, "currency": "USD" } }, "beneficiary": { "remittanceInformation": "Payment for services", "entity": { "type": "INDIVIDUAL", "firstName": "John", "lastName": "Dover", "dateOfBirth": "01-01-1980", "address": { "addressLine1": "123 Main St", "addressLine2": "Apt 4B", "region": "California", "city": "Los Angeles", "country": "US", "postCode": "90001" }, "relationshipType": "SELF_OWNED" }, "bankAccount": { "accountNumber": "2152209165", "accountType": "CHEQUE", "bank": { "identificationCode": "ATFEUS33", "nid": "021000021", "country": "US", "address": { "addressLine1": "383 MADISON AVENUE", "region": "California", "city": "New York", "country": "US", "postCode": "10001" } } } } } ``` When implemented correctly, the fee-charging process works as follows: 1. The system calculates the total transaction amount (payment amount + customer fee) 2. During payment processing, the customer fee is: * Deducted from the customer's wallet. * Transferred to your designated fee wallet. 3. The full payment amount (excluding fees) is sent to the recipient This approach ensures transparent fee management while maintaining accurate transaction accounting. Here's an example of how the customer fee is calculated: :::info 📘 Example calculation When you receive the amount of $100 from customer Jane and charge a $0.50 partner's customer fee, Jane must have a minimum balance of $100.75 in her wallet (transaction amount $100 + BVNK fee $0.25 + customer fee $0.50). After a successful transaction, * Jane's initial wallet balance was $100.75. The current balance is now $0. * Amount credited to your designated fee wallet is $0.50. ::: ## Important considerations * **Outgoing payments**: Currently, the customer fees are only supported for payment `type: OUT`. Make sure the customer's wallet has enough funds to cover the customer fee. If fees like network or customer fees cannot be covered, the transaction is cancelled. * **Fee Structure**: Customer fees are implemented as a flat fee, a fixed predetermined price, per transaction. * **Optional**: You are not required to charge customer fees. Transactions can be processed without additional fees if desired. ## Error handling If the customer's wallet does not have enough funds for the customer fee, you have two options: * Return an "insufficient funds" error to the user. In this case, the transaction is cancelled. * Reduce the transaction amount to accommodate the fee. This ensures the total (transaction amount plus fee) does not exceed the available balance. --- ## Configure webhooks Webhooks provide real-time, automated notifications for changes in your BVNK account. Configure them for key events, including: * Pay-ins received * Payouts sent * Refunds initiated * Transaction status changes This capability is essential for automating your workflows. For example, you can instantly credit a customer's wallet when a successful deposit webhook is received, eliminating the need for manual confirmation in the BVNK Portal. For the detailed description list of webhooks, see the [Webhooks API Reference](../../api-explorer/bvnk-webhooks/bvnk-webhooks-api). :::tip Most of the webhook events are enabled by default. However, you may need to contact the BVNK Solutions team to enable the following events: - [bvnk:payment:channel:transaction-on-hold](../../api-explorer/bvnk-webhooks/channel-transaction-on-hold/) - [bvnk:payment:crypto:transaction-on-hold](../../api-explorer/bvnk-webhooks/crypto-transaction-on-hold/) ::: ## Create webhooks To create a webhook, do the following: 1. Log in to the BVNK Portal. 2. On the sidebar, click **Integrations**, go to the **Webhooks** tab, and click **Add webhook**. ![](/img/bvnk/get-started/webhook-add-button.png) 3. In the **Add new webhook** dialogue, specify the following webhook settings: * **Description**: Provide a short, meaningful description for the webhook. * **Webhook URL to receive events**: Enter the Webhook URL to which the events will be sent. The URL must use HTTPS on port 443. Custom ports are not supported. * **Events to send**: Select the events to send from the list provided. ![](/img/bvnk/get-started/webhook-events-selection.png) Here, you can select events for operations related to fiat and crypto. 4. Click **Add**. The new active webhook appears in the list of webhooks where you can now manage the configuration going forward. You're all set. You will receive webhooks containing the details of the event to which you have subscribed. Also, see a [product demo](https://www.loom.com/share/801769cf55b649a5948590d13ede26bf?sid=b59e8813-49a4-4044-9327-867916088fd1) or read more in the [Help Centre article](https://help.bvnk.com/hc/en-us/articles/22943700946450-Webhook-Configuration-in-the-BVNK-Portal). :::tip No strict serialisation Do not strictly serialise the webhook fields that you do not need. Only apply the fields that you use in your platform. For example, if you do not need the `metadata` field, you can omit it from the response. ::: ## Update webhooks After you create a webhook, you can change its parameters, name or events. 1. Go to **Integration** > **Webhooks** and find the webhook you want to update. 2. In the selected webhook line, click three dots and click **Edit**. ![](/img/bvnk/get-started/webhook-edit.png) The **Update webhook** screen appears. 3. Change webhook parameters, for example, you can update its description or add and remove events. 4. Click **Update**. *** ## Create webhook listener BVNK sends webhooks for changes on transactions and other entities, so it's a good idea to set up a listener in your service to get the most out of our Payments API. Webhooks are sent as an HTTP POST with `Content-Type: application/json` containing the payload of the object. :::warning Important notes * Always check the status of the payment on the API after you receive a webhook, as further details may be required, such as the final amount paid for the transaction about which you have been notified. * After you receive the final webhook from BVNK, do not update transactions in your system to prevent duplicate transaction IDs from affecting your customers ::: ### Acknowledge events BVNK expects the `200` status code upon receipt of a successfully sent webhook. If your webhook script performs any logic after receiving a webhook, to prevent timeouts, you should return a `200` status code before completing the logic. ### Webhook retry policy If BVNK does not receive a `200` status code, the webhook retry policy will kick in. This includes cases where the endpoint returns a non-2xx status code or fails to respond within the **10-second** timeout window. After 10 seconds without a successful response, the delivery attempt is considered failed and will trigger the retry process defined by the webhook policy. The retry policy adds a delay before each retry, starting with a base delay and increasing it with each retry. The delay grows larger after each attempt, but will not exceed 15 minutes. The system will attempt up to 100 times, and after that, no further retries will occur. To ensure safe and consistent processing, all webhook events include a unique identifier that receivers should use to implement idempotency. This prevents duplicate event handling if the same webhook is retried multiple times due to timeouts or failed responses. The receiving system should detect repeated event IDs and avoid performing the same business action more than once. ### Handle duplicate events Callback endpoints might occasionally receive the same event more than once. We advise you to guard against duplicate event receipts. One way to do this is to log the events you've processed and then exclude the logged ones from further processing. ### Validate webhooks The webhook also contains a signature to allow servers to verify the authenticity of the request using a server-side secret key. The key is displayed in the field Public Key when you create the hook. The signature is present in the headers as the `x-signature` header. See the examples for different languages below. ```java import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.util.Arrays; public class Main { public static void main(String[] args) throws Exception { // Secret key from webhook configuration (used as string directly, not Base64 decoded) String secretKey = "JUL5QkQrpZYwW+Kf03QSrb70CG+ea/j8E/JfslENaXhd0AGBDbAtYYP2MdjkYDqiwjBtrtQXsUIbtAf7IGEGfg=="; // From X-Signature header String signatureBase64 = "VWEtiJu/7dLrq0ZtXS0HbGDPy6Re86oZeTnEEc9mlxg="; // Compact JSON payload (no spaces/newlines) - this is what the hook service signs String payload = "{\"event\":\"bvnk:ledger:report:ready\",\"eventId\":\"0198a8bb-7d56-79ef-92b2-2aed247c21b4\",\"timestamp\":\"2025-08-14T13:18:36.374620938Z\",\"data\":{\"id\":\"8d942264-e27e-44d9-a048-5ed66ac5129f\",\"url\":\"https://bvnk-staging-reports.s3.eu-west-1.amazonaws.com/transaction/8d942264-e27e-44d9-a048-5ed66ac5129f_7b2f87b9-1335-4b1e-bbeb-6a59ba1dc6df.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20250814T131836Z&X-Amz-SignedHeaders=host&X-Amz-Credential=AKIAZP6R7NJUFAGFGMNA%2F20250814%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Expires=604800&X-Amz-Signature=5c861e552b3e253be28f8e02c818b3c5e00269b9b50b6d4957d5281825d31a67\",\"type\":\"TRANSACTION\",\"status\":\"READY\"}}"; // Use secret key as UTF-8 bytes directly (as hook service does) byte[] secretKeyBytes = secretKey.getBytes("UTF-8"); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyBytes, "HmacSHA256"); // Initialize HMAC signature verification Mac mac = Mac.getInstance("HmacSHA256"); mac.init(secretKeySpec); // Generate signature for the payload byte[] computedSignature = mac.doFinal(payload.getBytes()); String computedSignatureBase64 = java.util.Base64.getEncoder().encodeToString(computedSignature); // Decode received signature byte[] receivedSignature = java.util.Base64.getDecoder().decode(signatureBase64); // Verify signatures match boolean verified = Arrays.equals(computedSignature, receivedSignature); System.out.println("Expected signature: " + computedSignatureBase64); System.out.println("Received signature: " + signatureBase64); System.out.println("Signature is valid: " + verified); } } ``` ```python #!/usr/bin/env python3 """ BVNK Webhook Signature Verification - Python This script verifies HMAC-SHA256 signatures from BVNK webhook payloads. """ import hmac import hashlib import base64 def verify_webhook_signature(secret_key, payload, signature_base64): """ Verify HMAC-SHA256 signature for webhook payload Args: secret_key (str): The shared secret from webhook configuration payload (str): The JSON payload as received in webhook signature_base64 (str): The signature from X-Signature header Returns: bool: True if signature is valid, False otherwise """ # Convert secret key to bytes using UTF-8 encoding secret_bytes = secret_key.encode('utf-8') # Convert payload to bytes using UTF-8 encoding payload_bytes = payload.encode('utf-8') # Generate HMAC-SHA256 signature computed_signature = hmac.new( secret_bytes, payload_bytes, hashlib.sha256 ).digest() # Base64 encode the computed signature computed_signature_base64 = base64.b64encode(computed_signature).decode('ascii') # Decode the received signature received_signature = base64.b64decode(signature_base64) # Compare signatures using constant-time comparison return hmac.compare_digest(computed_signature, received_signature) def main(): # Secret key from webhook configuration (used as string directly) secret_key = "JUL5QkQrpZYwW+Kf03QSrb70CG+ea/j8E/JfslENaXhd0AGBDbAtYYP2MdjkYDqiwjBtrtQXsUIbtAf7IGEGfg==" # From X-Signature header signature_base64 = "VWEtiJu/7dLrq0ZtXS0HbGDPy6Re86oZeTnEEc9mlxg=" # Compact JSON payload (no spaces/newlines) - this is what the hook service signs payload = '{"event":"bvnk:ledger:report:ready","eventId":"0198a8bb-7d56-79ef-92b2-2aed247c21b4","timestamp":"2025-08-14T13:18:36.374620938Z","data":{"id":"8d942264-e27e-44d9-a048-5ed66ac5129f","url":"https://bvnk-staging-reports.s3.eu-west-1.amazonaws.com/transaction/8d942264-e27e-44d9-a048-5ed66ac5129f_7b2f87b9-1335-4b1e-bbeb-6a59ba1dc6df.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20250814T131836Z&X-Amz-SignedHeaders=host&X-Amz-Credential=AKIAZP6R7NJUFAGFGMNA%2F20250814%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Expires=604800&X-Amz-Signature=5c861e552b3e253be28f8e02c818b3c5e00269b9b50b6d4957d5281825d31a67","type":"TRANSACTION","status":"READY"}}' # Verify the signature is_valid = verify_webhook_signature(secret_key, payload, signature_base64) # For debugging - compute expected signature secret_bytes = secret_key.encode('utf-8') payload_bytes = payload.encode('utf-8') computed_signature = hmac.new(secret_bytes, payload_bytes, hashlib.sha256).digest() expected_signature_base64 = base64.b64encode(computed_signature).decode('ascii') print(f"Expected signature: {expected_signature_base64}") print(f"Received signature: {signature_base64}") print(f"Signature is valid: {is_valid}") if __name__ == "__main__": main() ``` ```php ``` ```javascript JavaScript const crypto = require('crypto'); /** * Verify HMAC-SHA256 signature for webhook payload * * @param {string} secretKey - The shared secret from webhook configuration * @param {string} payload - The JSON payload as received in webhook * @param {string} signatureBase64 - The signature from X-Signature header * @returns {boolean} True if signature is valid, false otherwise */ function verifyWebhookSignature(secretKey, payload, signatureBase64) { // Generate HMAC-SHA256 signature using the secret key as UTF-8 bytes const computedSignature = crypto .createHmac('sha256', secretKey) .update(payload, 'utf8') .digest(); // Base64 encode the computed signature const computedSignatureBase64 = computedSignature.toString('base64'); // Decode the received signature const receivedSignature = Buffer.from(signatureBase64, 'base64'); // Compare signatures using constant-time comparison return crypto.timingSafeEqual(computedSignature, receivedSignature); } function main() { // Secret key from webhook configuration (used as string directly) const secretKey = "JUL5QkQrpZYwW+Kf03QSrb70CG+ea/j8E/JfslENaXhd0AGBDbAtYYP2MdjkYDqiwjBtrtQXsUIbtAf7IGEGfg=="; // From X-Signature header const signatureBase64 = "VWEtiJu/7dLrq0ZtXS0HbGDPy6Re86oZeTnEEc9mlxg="; // Compact JSON payload (no spaces/newlines) - this is what the hook service signs const payload = '{"event":"bvnk:ledger:report:ready","eventId":"0198a8bb-7d56-79ef-92b2-2aed247c21b4","timestamp":"2025-08-14T13:18:36.374620938Z","data":{"id":"8d942264-e27e-44d9-a048-5ed66ac5129f","url":"https://bvnk-staging-reports.s3.eu-west-1.amazonaws.com/transaction/8d942264-e27e-44d9-a048-5ed66ac5129f_7b2f87b9-1335-4b1e-bbeb-6a59ba1dc6df.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20250814T131836Z&X-Amz-SignedHeaders=host&X-Amz-Credential=AKIAZP6R7NJUFAGFGMNA%2F20250814%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Expires=604800&X-Amz-Signature=5c861e552b3e253be28f8e02c818b3c5e00269b9b50b6d4957d5281825d31a67","type":"TRANSACTION","status":"READY"}}'; // Verify the signature const isValid = verifyWebhookSignature(secretKey, payload, signatureBase64); // For debugging - compute expected signature const computedSignature = crypto .createHmac('sha256', secretKey) .update(payload, 'utf8') .digest('base64'); console.log(`Expected signature: ${computedSignature}`); console.log(`Received signature: ${signatureBase64}`); console.log(`Signature is valid: ${isValid}`); } // Example Express.js webhook handler function setupExpressWebhookHandler(app) { const express = require('express'); // Middleware to get raw body for signature verification app.use('/webhook', express.raw({ type: 'application/json' })); app.post('/webhook', (req, res) => { const payload = req.body.toString('utf8'); const signatureBase64 = req.headers['x-signature']; // Your secret key from webhook configuration const secretKey = "YOUR_SECRET_KEY_HERE"; if (verifyWebhookSignature(secretKey, payload, signatureBase64)) { // Signature is valid - process the webhook const data = JSON.parse(payload); console.log('Webhook verified and processed successfully'); // Process your webhook data here res.status(200).send('OK'); } else { // Invalid signature console.log('Invalid webhook signature'); res.status(401).send('Invalid signature'); } }); } // Browser version (if you need client-side verification - not recommended for security) async function verifyWebhookSignatureBrowser(secretKey, payload, signatureBase64) { // Convert secret key to ArrayBuffer const secretKeyBuffer = new TextEncoder().encode(secretKey); // Import the key for HMAC const key = await crypto.subtle.importKey( 'raw', secretKeyBuffer, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify'] ); // Convert payload to ArrayBuffer const payloadBuffer = new TextEncoder().encode(payload); // Generate signature const signatureBuffer = await crypto.subtle.sign('HMAC', key, payloadBuffer); // Convert to base64 const computedSignatureBase64 = btoa(String.fromCharCode(...new Uint8Array(signatureBuffer))); // Decode received signature const receivedSignature = Uint8Array.from(atob(signatureBase64), c => c.charCodeAt(0)); const computedSignature = new Uint8Array(signatureBuffer); // Compare signatures if (receivedSignature.length !== computedSignature.length) { return false; } let result = 0; for (let i = 0; i < receivedSignature.length; i++) { result |= receivedSignature[i] ^ computedSignature[i]; } return result === 0; } // Run the test if this file is executed directly if (require.main === module) { main(); } module.exports = { verifyWebhookSignature, verifyWebhookSignatureBrowser, setupExpressWebhookHandler }; ``` ```csharp using System; using System.Security.Cryptography; using System.Text; namespace BvnkWebhookVerification { public class WebhookVerifier { /// Verify HMAC-SHA256 signature for webhook payload public static bool VerifyWebhookSignature(string secretKey, string payload, string signatureBase64) { try { // Convert secret key to bytes using UTF-8 encoding byte[] secretKeyBytes = Encoding.UTF8.GetBytes(secretKey); // Convert payload to bytes using UTF-8 encoding byte[] payloadBytes = Encoding.UTF8.GetBytes(payload); // Generate HMAC-SHA256 signature using (var hmac = new HMACSHA256(secretKeyBytes)) { byte[] computedSignature = hmac.ComputeHash(payloadBytes); // Base64 encode the computed signature string computedSignatureBase64 = Convert.ToBase64String(computedSignature); // Decode the received signature byte[] receivedSignature = Convert.FromBase64String(signatureBase64); // Compare signatures using constant-time comparison return CryptographicOperations.FixedTimeEquals(computedSignature, receivedSignature); } } catch (Exception) { return false; } } /// /// Alternative signature verification for older .NET versions without CryptographicOperations /// public static bool VerifyWebhookSignatureLegacy(string secretKey, string payload, string signatureBase64) { try { byte[] secretKeyBytes = Encoding.UTF8.GetBytes(secretKey); byte[] payloadBytes = Encoding.UTF8.GetBytes(payload); using (var hmac = new HMACSHA256(secretKeyBytes)) { byte[] computedSignature = hmac.ComputeHash(payloadBytes); string computedSignatureBase64 = Convert.ToBase64String(computedSignature); // Simple string comparison (less secure but works for older .NET) return string.Equals(computedSignatureBase64, signatureBase64, StringComparison.Ordinal); } } catch (Exception) { return false; } } } class Program { static void Main(string[] args) { // Secret key from webhook configuration (used as string directly) string secretKey = "JUL5QkQrpZYwW+Kf03QSrb70CG+ea/j8E/JfslENaXhd0AGBDbAtYYP2MdjkYDqiwjBtrtQXsUIbtAf7IGEGfg=="; // From X-Signature header string signatureBase64 = "VWEtiJu/7dLrq0ZtXS0HbGDPy6Re86oZeTnEEc9mlxg="; // Compact JSON payload (no spaces/newlines) - this is what the hook service signs string payload = @"{""event"":""bvnk:ledger:report:ready"",""eventId"":""0198a8bb-7d56-79ef-92b2-2aed247c21b4"",""timestamp"":""2025-08-14T13:18:36.374620938Z"",""data"":{""id"":""8d942264-e27e-44d9-a048-5ed66ac5129f"",""url"":""https://bvnk-staging-reports.s3.eu-west-1.amazonaws.com/transaction/8d942264-e27e-44d9-a048-5ed66ac5129f_7b2f87b9-1335-4b1e-bbeb-6a59ba1dc6df.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20250814T131836Z&X-Amz-SignedHeaders=host&X-Amz-Credential=AKIAZP6R7NJUFAGFGMNA%2F20250814%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Expires=604800&X-Amz-Signature=5c861e552b3e253be28f8e02c818b3c5e00269b9b50b6d4957d5281825d31a67"",""type"":""TRANSACTION"",""status"":""READY""}}"; // Verify the signature bool isValid = WebhookVerifier.VerifyWebhookSignature(secretKey, payload, signatureBase64); // For debugging - compute expected signature byte[] secretKeyBytes = Encoding.UTF8.GetBytes(secretKey); byte[] payloadBytes = Encoding.UTF8.GetBytes(payload); using (var hmac = new HMACSHA256(secretKeyBytes)) { byte[] computedSignature = hmac.ComputeHash(payloadBytes); string expectedSignatureBase64 = Convert.ToBase64String(computedSignature); Console.WriteLine($"Expected signature: {expectedSignatureBase64}"); Console.WriteLine($"Received signature: {signatureBase64}"); Console.WriteLine($"Signature is valid: {isValid}"); } } } } ``` ```ruby Ruby require 'openssl' require 'base64' class WebhookVerifier ## # Verify HMAC-SHA256 signature for webhook payload # # @param secret_key [String] The shared secret from webhook configuration # @param payload [String] The JSON payload as received in webhook # @param signature_base64 [String] The signature from X-Signature header # @return [Boolean] True if signature is valid, false otherwise def self.verify_webhook_signature(secret_key, payload, signature_base64) # Generate HMAC-SHA256 signature using the secret key as UTF-8 bytes computed_signature = OpenSSL::HMAC.digest('SHA256', secret_key, payload) # Base64 encode the computed signature computed_signature_base64 = Base64.strict_encode64(computed_signature) # Decode the received signature received_signature = Base64.strict_decode64(signature_base64) # Compare signatures using constant-time comparison OpenSSL.secure_compare(computed_signature, received_signature) rescue StandardError false end ## # Alternative signature verification for older Ruby versions without secure_compare def self.verify_webhook_signature_legacy(secret_key, payload, signature_base64) computed_signature = OpenSSL::HMAC.digest('SHA256', secret_key, payload) computed_signature_base64 = Base64.strict_encode64(computed_signature) # Simple string comparison (less secure but works for older Ruby) computed_signature_base64 == signature_base64 rescue StandardError false end end def main # Secret key from webhook configuration (used as string directly) secret_key = 'JUL5QkQrpZYwW+Kf03QSrb70CG+ea/j8E/JfslENaXhd0AGBDbAtYYP2MdjkYDqiwjBtrtQXsUIbtAf7IGEGfg==' # From X-Signature header signature_base64 = 'VWEtiJu/7dLrq0ZtXS0HbGDPy6Re86oZeTnEEc9mlxg=' # Compact JSON payload (no spaces/newlines) - this is what the hook service signs payload = '{"event":"bvnk:ledger:report:ready","eventId":"0198a8bb-7d56-79ef-92b2-2aed247c21b4","timestamp":"2025-08-14T13:18:36.374620938Z","data":{"id":"8d942264-e27e-44d9-a048-5ed66ac5129f","url":"https://bvnk-staging-reports.s3.eu-west-1.amazonaws.com/transaction/8d942264-e27e-44d9-a048-5ed66ac5129f_7b2f87b9-1335-4b1e-bbeb-6a59ba1dc6df.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20250814T131836Z&X-Amz-SignedHeaders=host&X-Amz-Credential=AKIAZP6R7NJUFAGFGMNA%2F20250814%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Expires=604800&X-Amz-Signature=5c861e552b3e253be28f8e02c818b3c5e00269b9b50b6d4957d5281825d31a67","type":"TRANSACTION","status":"READY"}}' # Verify the signature is_valid = WebhookVerifier.verify_webhook_signature(secret_key, payload, signature_base64) # For debugging - compute expected signature computed_signature = OpenSSL::HMAC.digest('SHA256', secret_key, payload) expected_signature_base64 = Base64.strict_encode64(computed_signature) puts "Expected signature: #{expected_signature_base64}" puts "Received signature: #{signature_base64}" puts "Signature is valid: #{is_valid}" end # Example Sinatra webhook handler def setup_sinatra_webhook_handler require 'sinatra' require 'json' post '/webhook' do # Read the raw body request.body.rewind payload = request.body.read # Get the signature from headers signature_base64 = request.env['HTTP_X_SIGNATURE'] || '' # Your secret key from webhook configuration secret_key = 'YOUR_SECRET_KEY_HERE' if WebhookVerifier.verify_webhook_signature(secret_key, payload, signature_base64) # Signature is valid - process the webhook data = JSON.parse(payload) puts 'Webhook verified and processed successfully' # Process your webhook data here status 200 'OK' else # Invalid signature puts 'Invalid webhook signature' status 401 'Invalid signature' end end end # Example Rails controller class WebhooksController < ApplicationController skip_before_action :verify_authenticity_token def handle_webhook payload = request.raw_post signature_base64 = request.headers['X-Signature'] || '' # Your secret key from webhook configuration secret_key = Rails.application.credentials.webhook_secret_key if WebhookVerifier.verify_webhook_signature(secret_key, payload, signature_base64) # Signature is valid - process the webhook data = JSON.parse(payload) Rails.logger.info 'Webhook verified and processed successfully' # Process your webhook data here head :ok else # Invalid signature Rails.logger.error 'Invalid webhook signature' head :unauthorized end rescue JSON::ParserError Rails.logger.error 'Invalid JSON in webhook payload' head :bad_request rescue StandardError => e Rails.logger.error "Webhook processing error: #{e.message}" head :internal_server_error end end # Run the test if this file is executed directly main if __FILE__ == $PROGRAM_NAME ``` :::warning Make sure you do not parse your HTTP request into an object and then serialize it. Instead, provide the webhook raw payload as a string. ::: The signature will be available in the `x-signature` header of the webhook. Every new webhook has a unique `x-signature` header value. Make sure you compare the hash to the correct header. --- ## Create a wallet (v.1) :::warning DEPRECATED The Wallets v.1 functionality will be deprecated soon. However, you can still use it for existing integrations. For the new integrations, we recommend checking [Wallet v.2](../set-up-wallets). ::: BVNK offers capabilities to create and manage crypto wallets directly from within the BVNK portal. :::note When you create a fiat wallet, it is not available right away. Before you can use it, request your Account Manager for the wallet verification. ::: ## Create a wallet To create a wallet, do the following: To create a fiat or crypto wallet with an [assigned wallet profile](#assign-a-wallet-profile), send the [`POST /ledger/v1/wallets`](../../api-explorer/endpoints/create-customer-wallet) request using the following parameters: ```json { "currencyCode": "USD", "name": "ACH/FEDWIRE test", "customerReference": "09c04f1b-77b2-42cc-a0c0-da9748a3be11", "instruction": { "walletProfile": "2a3fe604-2b14-4009-90b3-1e48f1a38b11", "type": "FIAT", "virtualAccountRequired": true } } ``` ```json { "currencyCode": "USD", "name": "swift test", "customerReference": "09c04f1b-77b2-42cc-a0c0-da9748a3be11", "instruction": { "walletProfile": "18f9a1bf-0d77-45f5-b5ff-1e90411d88ce", "type": "FIAT", "virtualAccountRequired": true } } ``` ```json { "currencyCode": "USDT", "name": "USDT Test", "customerReference": "fb8c0b12-6552-429e-955e-70af08733310", "instruction": { "walletProfile": "f7fdb837-5053-45b0-86c8-96ece6fe02d0", "type": "CRYPTO", "virtualAccountRequired": false } } ``` In the successful response, you get the details of the new wallet: ```json Example Response for fiat wallet { "id": "a:25032877416178:1PL2yxP:1", "accountReference": "5bc49988-fe25-408f-bccc-3e0fe96cf322", "customerReference": "fb8c0b12-6552-429e-955e-70af0873331", "name": "USD ", "status": "ACTIVE", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [] } ``` ```json Example Response for crypto wallet { "id": "a:25065234155735:vE9Q6zE:1", "accountReference": "1274ab69-540c-4499-8b32-f51930c9bfed", "customerReference": "fb8c0b12-6552-429e-955e-70af08733310", "name": "USDT Test", "status": "ACTIVE", "balance": { "value": 0, "currencyCode": "USDT" }, "ledgers": [] } ``` The `id` of the created wallet is returned in a link via `response header`: `Location: https://api.sandbox.bvnk.com/ledger/v1/wallets/{id}`. By default the wallet has an `INACTIVE` status and should not be made available to the customer until it has been activated (`ACTIVE` status). The endpoint provided in the response header should be called periodically, to determine if the wallet has been activated as well as if a virtual account has been created and made available. See the [Wallet Read API endpoint](../../api-explorer/endpoints/wallet-read). The wallet can also have the `TERMINATED` status. This means the wallet can no longer be used, and that state is final; BVNK can never reactivate it. All operations, including receiving pay-ins, sending payouts, and currency conversions, are blocked for the terminated wallets. You can only access its transaction history. In case of a **fiat wallet**, the associated virtual account will be permanently closed with the banking partner. 1. On the BVNK Portal, go to **Wallets** and click **Add Wallet**. ![](/img/bvnk/get-started/add-wallet-button.png) 2. Select the currency, insert a unique name for this wallet and click **Create Wallet**. ![](/img/bvnk/get-started/create-wallet-form.png) The new wallet appears in the list. You also have a few functions available if you click on the wallet information: ![](/img/bvnk/get-started/wallet-created-list.png) :::info When you get access to your production BVNK portal account, fiat wallets will be automatically assigned to you during the onboarding process. ::: ## Assign a wallet profile To list the existing Wallet Profiles, send the [`GET /ledger/v1/wallets/profiles`](../../api-explorer/endpoints/wallet-profiles) request. You can filter by `currencyCodes` and `paymentMethods`. In the response, you get the Profile `reference` that you can assign to your customer's wallet based on the currency and payment method. ```json Fiat Wallets { "profiles": [ { "reference": "bd72bff2-6842-4212-9aa3-9298597d7d6e", "currencyCodes": [ "GBP" ], "paymentMethods": [ "FASTER_PAYMENT" ] }, { "reference": "c143fce7-a0e4-4475-86cd-ed37bc30efa5", "currencyCodes": [ "EUR" ], "paymentMethods": [ "SWIFT" ] }, { "reference": "18f9a1bf-0d77-45f5-b5ff-1e90411d88ce", "currencyCodes": [ "USD" ], "paymentMethods": [ "SWIFT" ] }, { "reference": "2a3fe604-2b14-4009-90b3-1e48f1a38b11", "currencyCodes": [ "USD" ], "paymentMethods": [ "ACH", "FEDWIRE" ] }, { "reference": "fe55d078-eaad-4c8b-a917-15170548df67", "currencyCodes": [ "EUR" ], "paymentMethods": [ "SEPA_CT", "SEPA_INST" ] } ] } ``` ```json Crypto Wallet (EU Entity) { "reference": "f7fdb837-5053-45b0-86c8-96ece6fe02d0", "currencyCodes": [ "USDT", "PYUSD", "POL", "BTC", "SOL", "BNB", "XRP", "ETH", "USDC", "LTC", "TRX" ], "paymentMethods": [] } ``` ```json Crypto Wallet (US Entity) { "reference": "a0b8a34c-bfb9-43d8-9ee4-6eb076d107c0", "currencyCodes": [ "USDT", "BTC", "SOL", "ETH", "USDC", "TRX" ], "paymentMethods": [] } ``` ## Fetch wallets The endpoint provides comprehensive information for each wallet, including status, balance, and other relevant details. This enables efficient monitoring and management of all payment channels linked to the customer, ensuring full operational visibility. You can list all wallets assigned to a customer and retrieve information for a specific wallet by providing its reference. ### All wallets To list all the wallets associated with a specific customer, send the [`GET /ledger/v1/wallets`](../../api-explorer/endpoints/ledger-wallet-list) request. ### Wallet by customer reference To retrieve wallet details by customer reference, send the [`GET ledger/v1/wallets?customerReference={customerReference}` ](../../api-explorer/endpoints/ledger-wallet-list) request with the `customerReference` query parameter. The response uses the same paginated format as the full wallet list above. ### Wallet by Wallet ID To retrieve wallet details by its unique identifier, send the [`GET ledger/v1/wallets/{id}`](../../api-explorer/endpoints/wallet-read) request with the `{id}` specified in the path. ## Wallet webhook To get notified when the a new wallet is created, listen to the [Ledger wallet creation notification](../../api-explorer/bvnk-webhooks/ledger-wallet-create). --- **What's next?** Now, when you have a wallet, you can start sending and receiving payments. - See the available [use cases](find-your-usecase.mdx) to select the one that best suits your needs. - Configure a designated wallet for [customer fees](charge-customer-fees.mdx). --- ## Select your delivery model This article outlines the available BVNK delivery models. Not sure which model is right for you? Take our interactive quiz to find out: ## Delivery models The direct model is the most basic delivery option. You contract directly with BVNK and access payment capabilities through the BVNK portal or API for your own use. You maintain full control over your payment flow and can customize it as needed. ![](/img/bvnk/get-started/direct-model.png) *Legal entity relationships in the Direct model* **Who is it for:** Companies that need to send, receive, or hold stablecoins for their customers. **Flow of funds:** Payments are sent from and to your treasury. **Integration options:** BVNK Portal or API. **Onboarding:** BVNK onboards you as the direct customer. **Use cases**: - [Send stablecoin payments](../../use-cases/stablecoin-payments-for-platforms/payout-overview) - [Receive payments](../../use-cases/stablecoin-payments-for-platforms/get-payment-overview) - [Convert funds (on-ramp)](../../use-cases/on-off-ramp/on-ramp-overview) - [Receive, send, and store funds with Virtual Accounts](../../use-cases/virtual-accounts/va-overview) You embed our payment capabilities into your platform via API integration and give your customers access to payments on leading blockchains and schemes. ![](/img/bvnk/get-started/custom-embedded-model.png) *Legal entity relationships in the Embedded model* **Who is it for:** Larger enterprises, such as platforms or marketplaces, seeking to embed stablecoins directly into their customer-facing ecosystem. **Flow of funds:** You can move funds on behalf of your customers, or they can move funds directly on your platform. **Integration options:** BVNK Portal or API **Onboarding:** - BVNK onboards you as a partner. - BVNK onboards your customers. - You submit onboarding docs for your customers. **Payment Instructions:** - A partner integrates via API. - A customer can send payment instructions via your platform. **Use cases**: - [Send stablecoin payments](../../use-cases/stablecoin-payments-for-platforms/payout-overview) - [Receive payments](../../use-cases/stablecoin-payments-for-platforms/get-payment-overview) - [Send payments via customers' wallets](../../use-cases/stablecoin-payments-for-platforms/epc-stablecoin-overview) - [Convert funds (on-ramp)](../../use-cases/on-off-ramp/on-ramp-overview) --- ## Find your use case Use this table to quickly identify which BVNK use case matches your business needs. Compare use cases to understand the target industries, currency flows, required delivery models, and available product features. Click the use case link to view detailed implementation guides, or use the Model links to learn more about the delivery models (Direct or Embedded) that support each use case. | **Use case** | **Use it to** | **For who?** | **Currency flow** | **Delivery model** | **Capabilities** | |:----------:|-------------|----------|----------|----------|----------| | **[Send stablecoin payments](../use-cases/stablecoin-payments-for-platforms/payout-overview.mdx)** | Pay out winnings and earning to customersSend stablecoins from fiat balanceSend stablecoins as a salary to employees and contractors | Gaming PlatformCFD / FX brokersTrading PlatformsNeobanksPayment Service Providers (PSPs) | fiat → cryptocrypto → crypto | [Direct](delivery-models.mdx?delivery-model=direct)[Embedded](delivery-models.mdx?delivery-model=custom-embedded) | SendReceiveConvert | | **[Receive stablecoin payments](../use-cases/stablecoin-payments-for-platforms/get-payment-overview.mdx)** | Collect payments from your users in stablecoins, while you still receive fiatAllow your users to top up their account in stablecoins | Payment Processing NetworksPayment Service Providers | crypto → fiatfiat → stablecoins | [Direct](delivery-models.mdx?delivery-model=direct)[Embedded](delivery-models.mdx?delivery-model=custom-embedded) | SendReceiveConvert | | **[Send stablecoin via customers' wallets](../use-cases/stablecoin-payments-for-platforms/epc-stablecoin-overview.mdx)** | Allow stablecoin payouts from your customers to their users from a pre-funded fiat balance | Payment Service Providers | crypto → cryptofiat → stablecoins | [Embedded](delivery-models.mdx?delivery-model=custom-embedded) | SendReceiveConvertStore | | **[Convert funds (on-ramp)](../use-cases/on-off-ramp/on-ramp-overview.mdx)** | Allow your customers to pay out in crypto directly from your platform | Payment Service Providers Employer of Record servicesMarketplacesNeobanks | crypto → fiatfiat → crypto | [Direct](delivery-models.mdx?delivery-model=direct)[Embedded](delivery-models.mdx?delivery-model=custom-embedded) | SendReceiveConvert | | **[Provide virtual account to your customers](../use-cases/virtual-accounts/va-overview.mdx?va=cVA)** | Enable customers to send and receive fiat funds for crypto tradingProcess 3rd party payments with optional stablecoin pre-funding | Payment Service ProvidersEmployer of Record servicesMarketplacesNeobanksCFD / FX brokersTrading Platforms | fiat → fiat | [Direct](delivery-models.mdx?delivery-model=direct)[Embedded](delivery-models.mdx?delivery-model=custom-embedded) | SendReceiveStore | | **[Receive, send, and store funds with Virtual Accounts](../use-cases/virtual-accounts/va-overview.mdx?va=VA)** | Create payment accounts with unique vIBAN for your businessReceive deposits in stablecoin and pay in fiat | Payment Service ProvidersEmployer of Record servicesMarketplacesNeobanksCFD / FX brokersTrading Platforms | fiat → fiat | [Direct](delivery-models.mdx?delivery-model=direct)[Embedded](delivery-models.mdx?delivery-model=custom-embedded) | SendReceiveStore | | **[Issue cards to customers](../use-cases/cards/cards.mdx)** | Issue stablecoin-backed virtual spending cards to your customersEnable customers to spend stablecoin at merchants via digital wallets | Payment Service ProvidersPlatforms and fintechsNeobanksRemittance platforms | crypto → fiat | [Embedded](delivery-models.mdx?delivery-model=custom-embedded) | SendStore | {/* | **[Enable Express wallets for customers](../use-cases/xpress-store-funds/express-overview.mdx)** | Start providing stablecoin services with a fast onboarding processOffer stablecoin storage and management with stored-value balances to your customers | Payment Service Providers (PSPs)Platforms and marketplacesNeobanks and financial services | fiat → stablecoinscrypto → crypto | [Embedded Express](delivery-models.mdx?delivery-model=embedded-express) | SendReceiveConvertStore | */} --- ## Create API keys BVNK uses **Hawk authentication** for secure API access. Hawk is a cryptographic scheme that provides request integrity and authentication without requiring you to send your secret key with each request. Instead, you use your Hawk Auth ID and Secret Key to generate a signature for each API request. This approach ensures that your credentials remain secure even if your requests are intercepted. Once the BVNK sandbox account is active, create API keys to call BVNK endpoints: 1. Log in to the [BVNK Portal](https://app.sandbox.bvnk.com/login). 2. On the sidebar, select **Integrations** and click **Generate API Key**. If 2FA is not enabled in your account yet, you'll be requested to configure it in an authenticator app. ![](/img/bvnk/get-started/enable-2fa.png) 3. In the **Generate new API key** window, specify the following parameters: * **Name**: Enter the description to identify the key in future. * **Allowed IP addresses**: Specify whitelisted IPs that your service will use to restrict IP addresses that can query the API. This improves security by ensuring that only requests from your own servers or trusted networks can authenticate with the API. **This must be set to enable withdrawals**. You can specify: * Static IP address, for example, `83.104.30.154`, or * Range of addresses using the CIDR (Classless Inter-Domain Routing) format, for example, `83.104.30.0/24`
Using CIDR notation CIDR format defines a network prefix and the size of the address block. It consists of an IP address, followed by a slash (/), and then a number (prefix length). The prefix length tells you how many bits of the address are fixed (the network part). The remaining bits are available for host addresses within that network. For example: * `83.104.30.0/24`: Prefix length `/24` means the first 24 bits (or first three octets) are fixed. This covers all IPs from `83.104.30.0` to `83.104.30.255` (256 addresses will have API access). * `83.104.0.0/16`. Prefix length `/16` means the first 16 bits (or first two octets) are fixed. This covers 83.104.0.0 to 83.104.255.255 (65,536 addresses will have API access). Avoid using overly broad ranges (like `/8` or `/0`), as this significantly weakens security as the access is provided to over 16 million addresses.
* **Withdrawals**: Click the toggle to enable or disable the use of these keys for withdrawals and payouts. If withdrawals are disabled, you will only be able to use these keys to collect payments. **Allowed IP addresses** must also be specified to enable withdrawals. ![](/img/bvnk/get-started/generate-key.png) 4. Click **Continue**. You will be prompted to enter the 6-digit code from a configured F2A app. ![](/img/bvnk/get-started/confirm-key.png) 5. API Keys are now generated, and the Hawk Auth ID and Secret Key should be saved. ![](/img/bvnk/get-started/generated-api-key.png) :::warning Save your keys immediately Once you've created the keys, you'll see the `Hawk Auth ID` and `Hawk Auth Key`. Store these credentials privately, as you won't be able to access the `Hawk Auth Key` again once you navigate away from this page. ::: --- **What's next?** Next, you can: - [Set up your first wallet](../set-up-wallets) for payment operations or request it from your Account Manager. - [Create accounts](../create-embedded-partner-customer/creating-an-embedded-customer-company-api) for your customers and [onboard them](../create-embedded-partner-customer/onboard-epc) to ensure regulatory compliance. - Find your use case and [start building](../find-your-usecase). --- ## Get started with BVNK Begin seamless and efficient stablecoin payments with BVNK. This page outlines the five steps from initial setup to going live: 1. [Contact your Account Manager](#contact-your-account-manager). 2. [Design your implementation](#design-your-implementation). 2. [Create a sandbox account](#create-a-sandbox-account) on the BVNK Portal. 3. [Integrate with BVNK services](#integrate-with-bvnk-services) and test your integration. 4. [Apply for production access and go live](#go-live). ## Contact your Account Manager Schedule a kick-off meeting with your [Account Manager](https://bvnk.com/contact) to discuss your requirements and preferred integration approach. ## Design your implementation Collaborate with your Account Manager and Solutions Consultant during the discovery and integration phase to: - [Decide on the delivery model](delivery-models.mdx). Manage stablecoins and compliance yourself, or delegate to BVNK. - Define your payment flows, set up fees, and fee schedules. - [Find the right use case](find-your-usecase.mdx) to match your needs. ## Create a sandbox account Your sandbox is a test environment that mirrors the production platform but operates with test data and virtual funds. Use it to: - Test API integrations safely. - Experiment with different features. - Validate your implementation before going live. To build and check your integration in the test environment, you need a BVNK sandbox account. Contact your Account Manager to request it. After you sign in to your account on the BVNK Portal, you can: - [Generate API keys](generate-api-keys.mdx) for API requests. - [Configure webhooks](create-webhook-listener.mdx) for real-time notifications about transactions and other events. - [Create accounts](create-embedded-partner-customer/creating-an-embedded-customer-company-api.mdx) for your customers and [onboard them](create-embedded-partner-customer/onboard-epc.mdx) to ensure regulatory compliance. Only for the [Embedded model](../delivery-models/?delivery-model=custom-embedded). - [Set up wallets](set-up-wallets.mdx) for your customers or request fiat wallets from BVNK's Solutions team. - Send and request payments. - Generate transaction reports. - Manage your customers and contacts. ## Integrate with BVNK services Check our [API reference](../../api-explorer/api-overview/overview) to integrate the BVNK functionality into your platform. To validate your integration in the sandbox environment: - [Set up a Metamask Ethereum wallet for ETH and USDT ERC20](set-up-metamask-ethereum-wallet-for-eth-and-usdt-erc20-tokens.mdx). - [Set up a TronLink wallet for TRX and USDT TRC20](set-up-tronlink-wallet-for-trx-and-usdt-trc20-tokens.mdx). ## Go live After building and testing your integration in the sandbox environment, you are ready to go live. Contact your Account Manager to activate your live account. :::important Sandbox configuration doesn't automatically transfer to your live account. You must manually replicate your setup. Before going live, verify: - API keys are configured in the live environment. - Webhook URLs are set up to ensure real-time notifications. - Transaction limits and settings meet operational needs. ::: Test your integration in the sandbox environment before full launch. --- ## Manage wallets ## Receive the webhook To get notified each time a wallet's status changes, listen to the [wallet notification](../../api-explorer/bvnk-webhooks/ledger-wallet-status-change-v-2). --- ## Migrate from Wallets v.1 to v.2 The Wallets API v.2 replaces v.1 endpoints, offering a simpler request model, more detailed responses, advanced Lucene-based filtering, and enhanced wallet lifecycle management. Migrating to v.2 streamlines your integration, reduces complexity, provides better reporting data, and enables more flexible wallet operations. This guide walks you through the required changes for a successful migration. ## Endpoint mapping | Operation | v.1 endpoint | v.2 endpoint | | :--- | :--- | :--- | | Create wallet | [`POST /ledger/v1/wallets`](../../api-explorer/endpoints/create-customer-wallet) | [`POST /ledger/v2/wallets`](../../api-explorer/endpoints/ledger-wallet-create-v-2) | | List wallets | [`GET /ledger/v1/wallets`](../../api-explorer/endpoints/ledger-wallet-list) | [`GET /ledger/v2/wallets`](../../api-explorer/endpoints/ledger-wallet-list-v-2) | | Get wallet | [`GET /ledger/v1/wallets/{id}`](../../api-explorer/endpoints/ledger-wallet-read) | [`GET /ledger/v2/wallets/{id}`](../../api-explorer/endpoints/ledger-wallet-read-v-2) | | Get profiles | [`GET /ledger/v1/wallets/profiles`](../../api-explorer/endpoints/wallet-profiles) | [`GET /ledger/v2/wallets/profiles`](../../api-explorer/endpoints/wallet-profiles-2) | | Manage wallet |— | [`POST /ledger/v2/wallets/{id}/actions`](../../api-explorer/endpoints/ledger-wallet-execute-action) | ## What changed ### Create wallet The v.2 wallet create request now uses simple, top-level fields instead of the nested `instruction` object. ```json { "currencyCode": "USD", "name": "USD SWIFT", "customerReference": "09c04f1b-77b2-42cc-a0c0-da9748a3be11", "instruction": { "walletProfile": "18f9a1bf-0d77-45f5-b5ff-1e90411d88ce", "type": "FIAT", "virtualAccountRequired": true } } ``` ```json { "currency": "USD", "name": "USD SWIFT", "customerId": "09c04f1b-77b2-42cc-a0c0-da9748a3be11", "profileId": "fiat:usd:141d6bae" } ``` **Field changes:** | v.1 field | v.2 field | Notes | | :--- | :--- | :--- | | `currencyCode` | `currency` | Same currency codes, renamed field. | | `customerReference` | `customerId` | Now optional: omit for direct (non-customer) wallets. | | `instruction.walletProfile` | `profileId` | Moved to top level. `profileId` replaces `walletProfile`, and profile IDs changed formats. See the [Wallet profiles](#wallet-profiles) section. | | `instruction.type` |—| Removed. The wallet type is determined by the selected `profileId`. | | `instruction.virtualAccountRequired` |— | Removed. Virtual accounts are automatically provisioned for fiat profiles. | ### Create wallet response The response format now uses clearer names and gives you more useful details. ```json { "id": "a:25032877416178:1PL2yxP:1", "accountReference": "5bc49988-fe25-408f-bccc-3e0fe96cf322", "customerReference": "fb8c0b12-6552-429e-955e-70af0873331", "name": "USD SWIFT", "status": "ACTIVE", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [ { "type": "FIAT", "accountHolderName": "Embedded customer A", "accountNumber": "900768107004", "code": "101019644", "accountNumberFormat": "ABA", "paymentReference": null } ] } ``` ```json { "id": "a:25060945127830:rTgK3wN:1", "name": "USD SWIFT", "customer": { "id": "a1f84b6c-9e23-4d17-b590-7c4a83de2f10", "name": "Meridian Healthcare" }, "status": "INACTIVE", "balance": { "amount": 0.00, "currency": "USD" }, "paymentInstruments": [ { "type": "FIAT", "accountHolderName": "MERIDIAN HEALTHCARE SERVICES LTD", "accountNumber": "GB29MDTR60000131926819", "remittanceInformationPrefix": "REF HK4F291", "bankDetails": { "name": "Meridian Trust Bank PLC", "address": { "addressLine1": "250 Bishopsgate", "city": "London", "country": "GB", "postCode": "EC2M 4AA" }, "bic": "MDTRGB2LXXX", "nid": { "value": "600001", "type": "SORT_CODE" } } } ], "createdAt": "2025-06-09T14:05:12.830Z", "updatedAt": "2025-06-09T14:05:12.830Z" } ``` **Response field changes:** | v.1 field | v.2 field | Notes | | :--- | :--- | :--- | | `accountReference` |— | Removed. | | `customerReference` | `customer.id` | Now a nested object with `id` and `name`. | | `balance.value` | `balance.amount` | Renamed. | | `balance.currencyCode` | `balance.currency` | Renamed. | | `ledgers` | `paymentInstruments` | Renamed; includes richer `bankDetails` for fiat. Crypto payment instruments are no longer returned. | |— | `createdAt` | New. ISO 8601 timestamp. | |— | `updatedAt` | New. ISO 8601 timestamp. | :::info New wallet status behavior In v.2, newly created wallets start as `INACTIVE` and transition to `ACTIVE` asynchronously. Listen to the [`ledger:v2:wallet:status-change`](../../api-explorer/bvnk-webhooks/ledger-wallet-status-change-v-2) webhook to know when the wallet is ready. ::: ### Wallet profiles Profile IDs now use descriptive string identifiers instead of UUIDs. **Request:** `GET /ledger/v1/wallets/profiles?currencyCodes=USD&paymentMethods=SWIFT` ```json { "profiles": [ { "reference": "18f9a1bf-0d77-45f5-b5ff-1e90411d88ce", "currencyCodes": ["USD"], "paymentMethods": ["SWIFT"] } ] } ``` **Request:** `GET /ledger/v2/wallets/profiles?q=currency:USD AND method:SWIFT` ```json { "totalElements": 1, "totalPages": 1, "content": [ { "id": "fiat:usd:141d6bae", "currencies": ["USD"], "methods": ["SWIFT"] } ], "pageable": { "pageNumber": 0, "pageSize": 50 }, "hasNext": false } ``` **Profile field changes:** | v.1 field | v.2 field | Notes | | :--- | :--- | :--- | | `reference` | `id` | Renamed. Format changed from UUID to descriptive string (e.g., `fiat:usd:141d6bae`). | | `currencyCodes` | `currencies` | Renamed. | | `paymentMethods` | `methods` | Renamed. | **Filtering:** v.1 uses query parameters (`currencyCodes`, `paymentMethods`). Wallets v.2 use the `q` parameter with [Lucene query syntax](https://lucene.apache.org/core/2_9_4/queryparsersyntax.html) (e.g., `currency:USD AND method:SWIFT`). ### List wallets Filtering moved from dedicated query parameters to a single `q` parameter with Lucene syntax. ```http GET /ledger/v1/wallets?customerReference=09c04f1b-77b2-42cc-a0c0-da9748a3be11¤cyCode=USD ``` ```http GET /ledger/v2/wallets?q=customerId:09c04f1b-77b2-42cc-a0c0-da9748a3be11 AND currency:USD ``` **Available v.2 filters via `q`:** - `customerId`: UUID of the customer - `status`: `ACTIVE`, `INACTIVE`, or `TERMINATED` - `currency`: currency code (e.g., `USD`, `USDT`) - `currencyType`: `FIAT` or `CRYPTO` Combine filters with `AND`/`OR` operators. :::note List response does not include payment instruments `GET /ledger/v2/wallets` returns wallet summaries only. To get full details, including `paymentInstruments`, send the [`GET /ledger/v2/wallets/{id}`](../../api-explorer/endpoints/ledger-wallet-read-v-2) request for each wallet. ::: ### Wallet lifecycle management (new in v.2) Wallets v.2 introduce [`POST /ledger/v2/wallets/{id}/actions`](../../api-explorer/endpoints/ledger-wallet-execute-action) for managing wallet states. In v.1, wallet termination required contacting support. Available actions: - `TERMINATE`: permanently closes the wallet. - `BLOCK`: temporarily suspends all wallet activity. - `UNBLOCK`: reactivates a blocked wallet. ```json { "action": "TERMINATE", "comment": "CLIENT_REQUEST" } ``` ### Webhooks | v.1 webhook | v.2 webhook | Notes | | :--- | :--- | :--- | | `ledger:wallet:create` | [`ledger:v2:wallet:status-change`](../../api-explorer/bvnk-webhooks/ledger-wallet-status-change-v-2) | v.2 fires on every status transition (creation, activation, termination, block/unblock), not only on creation. | ## Migration checklist 1. **Update profile retrieval**: replace [`GET /ledger/v1/wallets/profiles`](../../api-explorer/endpoints/wallet-profiles) with [`GET /ledger/v2/wallets/profiles`](../../api-explorer/endpoints/wallet-profiles-2). Map the new `id` field (replaces `reference`) into your wallet creation flow. 2. **Update wallet creation**: replace [`POST /ledger/v1/wallets`](../../api-explorer/endpoints/create-customer-wallet) with [`POST /ledger/v2/wallets`](../../api-explorer/endpoints/ledger-wallet-create-v-2). Flatten the request body: use `currency`, `customerId`, and `profileId` instead of the nested `instruction` object. 3. **Update response parsing**: adapt to the new response schema: - `balance.value` → `balance.amount` - `balance.currencyCode` → `balance.currency` - `customerReference` → `customer.id` - `ledgers` → `paymentInstruments` 4. **Update wallet listing**: replace [`GET /ledger/v1/wallets`](../../api-explorer/endpoints/ledger-wallet-list) with [`GET /ledger/v2/wallets`](../../api-explorer/endpoints/ledger-wallet-list-v-2). Convert query parameters to Lucene syntax in the `q` parameter. 5. **Update wallet retrieval**: replace [`GET /ledger/v1/wallets/{id}`](../../api-explorer/endpoints/ledger-wallet-read) with [`GET /ledger/v2/wallets/{id}`](../../api-explorer/endpoints/ledger-wallet-read-v-2). 6. **Subscribe to the v.2 webhook**: replace `ledger:wallet:create` with [`ledger:v2:wallet:status-change`](../../api-explorer/bvnk-webhooks/ledger-wallet-status-change-v-2). Update your webhook handler to process status transitions (`INACTIVE` → `ACTIVE`, `ACTIVE` → `TERMINATED`, etc.). 7. **Adopt lifecycle management**: if you previously contacted support to terminate wallets, use [`POST /ledger/v2/wallets/{id}/actions`](../../api-explorer/endpoints/ledger-wallet-execute-action) directly. --- **Related guides:** - [Set up wallets (v.2)](../set-up-wallets): full v.2 integration guide. - [Create a wallet (v.1)](../creating-your-first-wallet): wallet status webhook details. --- ## Try fiat payments in simulator The sandbox environment provides a consistent simulation experience for pay-ins and payouts across all customer accounts. This ensures predictable behaviour regardless of the underlying partner or integration method. The simulation also supports testing in cases where a partner's sandbox environment is limited or unavailable. This allows you to validate payment flows and integration logic in a controlled setting before going live. Note that this functionality is not enabled for all wallets. If you experience any issues when trying to use this simulator, contact Support. ## Pay-in simulator To simulate pay-ins with different currencies and payment methods, in the sandbox environment, send the [`POST /payment/v2/payins/simulation`](../../api-explorer/endpoints/simulate-payin-v-2/) request with the following payload: ```json { "walletId": "acc:23090539331497:bVsk6:0", "remittanceInformation": "Invoice INV-2024-00142", "method": "FASTER_PAYMENT", "amount": 60.50, "currency": "GBP", "originator": { "name": "James Whitfield", "bankAccount": { "accountNumber": "GB87SYPE04082500000904", "accountNumberFormat": "IBAN", "bankCode": "SRLGGB2L" } } } ``` ```json { "walletId": "acc:23090539331497:bVsk6:0", "remittanceInformation": "Payment for order 78432", "method": "SEPA_INST", "amount": 100, "currency": "EUR", "originator": { "name": "Sophie Muller", "bankAccount": { "accountNumber": "IE29AIBK93115212345678", "accountNumberFormat": "IBAN", "bankCode": "AIBKIE2D" } } } ``` ```json { "walletId": "acc:23090539331497:bVsk6:0", "remittanceInformation": "Ref 90215 - Monthly subscription", "method": "ACH", "amount": 120.60, "currency": "USD", "originator": { "name": "Michael Torres", "bankAccount": { "accountNumber": "026009593", "accountNumberFormat": "ABA", "bankCode": "BOFAUS3N" } } } ``` ```json { "walletId": "acc:23090539331497:bVsk6:0", "remittanceInformation": "Wire transfer - Contract 2024-A1", "method": "SWIFT", "amount": 2000.40, "currency": "USD", "originator": { "name": "Horizon Trading Ltd", "bankAccount": { "accountNumber": "US64BOFA0260095931234567890", "accountNumberFormat": "IBAN", "bankCode": "BOFAUS3N" } } } ``` The details of a `beneficiary` are unnecessary for simulating a pay-in because `walletId` provides specific information for the wallet to be credited. It is also advisable to subscribe to the pay-in webhook to stay informed about changes to the wallet balance resulting from the simulated pay-in. See [Configure webhooks](../create-webhook-listener) for more information. :::info 📘 Not all USD wallets support all payment methods See the process of Wallet Profiles. All payment methods and currencies are subject to change and enhancements with further optionality. ::: ## Payout simulator The standard payout flow is shown on the diagram: ![](/img/bvnk/test-integration/payout-simulator-flow.png) In the BVNK simulator, you can test the following behaviours: | Flow | Customer Reference Prefix | Description | | :--- | :------------------------ | :---------- | | NORMAL | Not Applicable | Standard process that does not require a prefix to simulate a payout. | | FAILED | `FAILED` | This flow is used to simulate a failure on the banking partner side such as the payment scheme rejecting the payout. | | RETURN | `RETURN` | This process simulates a successful payout, followed by a return event.In this scenario, the payout initially appears to be successful, as it has been submitted to the beneficiary's bank. However, the bank ultimately rejects the payment, or the customer decides to reverse the transaction. | To test the previously mentioned behaviors in the sandbox environment, you can input the **customer reference prefix** from the flow you want to simulate. If you do not include a customer reference prefix, the system will default to the `NORMAL` flow as the expected behavior. For instance, if you wish to simulate a failed flow when requesting a payout, the reference should be formatted as `FAILED`. --- ## Add Ethereum wallet for ETH and USDT/USDC To simulate payment transactions using the Payments API in sandbox, set up a MetaMask Ethereum wallet and add test tokens to it. This process allows you to experiment with and verify the API's functionality in a sandbox environment. ## Install MetaMask extension 1. [Install Metamask extension](https://metamask.io/download/) for your browser, Chrome is recommended. Follow the steps carefully and save your password and recovery codes in a safe place. ![](/img/bvnk/test-integration/metamask-1-install.png) 2. Optional. Pin the extension to your browser by clicking the puzzle icon > pin icon next to MetaMask. ![](/img/bvnk/test-integration/metamask-2-pin.png) 3. Click the fox-head icon to open your MetaMask wallet and log in. ![](/img/bvnk/test-integration/metamask-3-open.png) 4. By default, the Ethereum Mainnet network is selected. To change to another one, click the **Networks** dropdown at the top. ![](/img/bvnk/test-integration/metamask-4-networks.png) 5. The **Select a network** tab opens. Click the slider to show test networks, then select **Sepolia**. ![](/img/bvnk/test-integration/metamask-5-testnet.png) 6. The SepoliaETH token name indicates that you are on the Sepolia Network. Now you can receive funds and test Ethereum transactions in the sandbox. ![](/img/bvnk/test-integration/metamask-6-sepolia.png) *** ## Add tokens To add USDT ERC20 tokens to the wallet, do the following: 1. Import tokens by adding the smart contract to your wallet. For that, in your MetaMask wallet, on the **Tokens** tab, click **Import tokens**. ![](/img/bvnk/test-integration/metamask-7-import-tokens.png) 2. The import tokens screen opens. In the **Token contract address** field, add `0x3429519eE7cDbB13B49161F1Eb6e1B026939113A`. The **Token symbol** and **Token Decimal** are auto-completed. If not, enter **USDT** and **6** respectively. ![](/img/bvnk/test-integration/metamask-8-token-contract.png) 3. When the prompt appears, click **Import**. ![](/img/bvnk/test-integration/metamask-9-import-prompt.png) USDT ERC20 is now added to your wallet, and you can complete USDT transactions once you have received test funds. ![](/img/bvnk/test-integration/metamask-10-usdt-added.png) ETH and USDT ERC20 on the Sepolia Network are now available on your MetaMask wallet. Get some test funds loaded and happy testing! ![](/img/bvnk/test-integration/metamask-11-complete.png) To add USDC ETHEREUM tokens to the wallet, do the following: 1. Import tokens by adding the smart contract to your wallet. For that, in your MetaMask wallet, on the **Tokens** tab, click **Import tokens**. ![](/img/bvnk/test-integration/usdc-import-tokens.png) 2. The import tokens screen opens. Configure the fields as follows: - **Token contract address**: `0x6988F886eC2706893d37eE169722DdB02135eF78` - **Token symbol**: USDC ![](/img/bvnk/test-integration/usdc-add-address.png) Click **Next**. 3. When the prompt appears, click **Import**. ![](/img/bvnk/test-integration/usdc-import-confirmation.png) USDC ETHEREUM is now added to your wallet, and you can complete USDC transactions once you have received test funds. ![](/img/bvnk/test-integration/usdc-token-imported.png) --- ## Add TronLink wallet for TRX and USDT To simulate payment transactions using the Payments API in sandbox, set up a TronLink wallet and add test tokens to it. This process allows you to experiment with and verify the API's functionality in a sandbox environment. ## Install TronLink extension 1. Go to [Chrome Web Store](https://chromewebstore.google.com/detail/tronlink/ibnejdfjmmkpcnlpebklmnkoeoihofec), and on the TronLink extension page, click **Add to Chrome**. 2. Optional. Pin the extension to your browser by clicking the **Puzzle** icon > **Pin** icon next to **TronLink**. ![](/img/bvnk/test-integration/tronlink-1-pin.png) 3. Click the **TronLink** icon to open the extension, and then click **Create Wallet**. Follow the steps carefully and save your password and recovery codes in a safe place. ![](/img/bvnk/test-integration/tronlink-2-create-wallet.png) 4. By default, the Tron Mainnet network is selected. To change to another one, click the **Networks** dropdown at the top. ![](/img/bvnk/test-integration/tronlink-3-networks.png) 5. The **Networks** tab opens. Select **TRON Nile Testnet**. On the first run, the test networks are hidden. To enable them, go to **Settings** > **Networks** and click the **Show Test Networks** toggle. ![](/img/bvnk/test-integration/tronlink-4-testnet.png) The **TRON Nile Testnet** (TRX) at the top indicates you are now on the Nile TestNet. Now you can receive funds and test transactions in the sandbox. ![](/img/bvnk/test-integration/tronlink-5-nile-testnet.png) *** ## Add TRON to your wallet To activate your TronLink account, add TRON test coins to it. For that, 1. In the TronLink extension, copy the wallet address. ![](/img/bvnk/test-integration/tronlink-nile-testnet-address.png) 2. Go to [Nile Testnet](https://nileex.io/join/getJoinPage) and enter your TronLink wallet address. ![](/img/bvnk/test-integration/tronlink-6-nile-testnet-faucet.png) 2000 test Tron coins will be sent to your wallet. ## Add BVNK USDT TRC20 To add USDT TRC20 tokens to your TronLink wallet, import the tokens created by BVNK by adding the smart contract to your wallet. To add a smart token, do the following: 1. Go to [Tronscan](https://nile.tronscan.org/), and click **Connect Wallet** on the top right. ![](/img/bvnk/test-integration/tronlink-7-connect-wallet.png) 2. In the **Search** bar, enter `TY1DBj7Ys1bDcK37kwATaQpHxdTCnYrr1f`.\ The **LAYER1** token appears. This is a BVNK test USDT token that can be used as a USDT TRC20. ![](/img/bvnk/test-integration/tronlink-8-token-search.png) 3. Go to the **Contract** tab > **Write Contract**, and select **mint**. Enter the amount of tokens to receive (for example, 50.000), and click **Send** ![](/img/bvnk/test-integration/tronlink-10-mint.png) 4. The **Trigger Smart Contract** window will appear in the browser. Click **Sign**. ![](/img/bvnk/test-integration/tronlink-11-sign.png) 5. Once the transaction has gone through, open your TronLink extension in Chrome, and next to **Assets**, click the "**+**" sign. ![](/img/bvnk/test-integration/tronlink-12-add-assets.png) 6. In the **Assets** list, in the **New** section, select **LAYER1**, and click the "**+**" sign. ![](/img/bvnk/test-integration/tronlink-13-select-layer1.png) You now have BVNK LAYER 1 (USDT TRC20) Testnet tokens that can be used in transactions via API and the BVNK portal for both payments in and out. You've successfully acquired BVNK LAYER 1 (USDT TRC20) Testnet tokens. These tokens are designed for testing purposes and can be utilised in various ways: * Simulate transactions through the API. * Test payment flows within the BVNK Portal. * Experiment with both incoming and outgoing payments. This allows you to thoroughly evaluate the platform's functionality in a risk-free environment before engaging with real assets. --- ## Set up wallets :::info Wallets API v.2 This is a new wallets flow, which is recommended for all new integrations. If you already have an existing integration with [Wallets API v.1](../../api-explorer/endpoints/create-customer-wallet), you can continue using it. For the Wallets v.1 guide see [Create a wallet](../creating-your-first-wallet). ::: On BVNK, you can create and manage both crypto and fiat wallets through the Portal or API. Specify a customer's ID and reference to create wallets for them; otherwise, the wallet is created for your own account. When created, a wallet passes the following stages: ```mermaid %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#ffffff', 'lineColor': '#3071F2', 'primaryTextColor': '#344054', 'fontSize': '15px'}, 'flowchart': {'padding': 15}}}%% flowchart LR A["1. Retrieve wallet profiles*


GET#nbsp;/ledger/v2/wallets/profiles"] --> B["2. Create a wallet


POST#nbsp;/ledger/v2/wallets"] B --> C["3. Receive ID in response; wait for wallet activation

#quot;status#quot;: #quot;INACTIVE#quot;"] C --> D["4. Receive status webhook

ledger:v2:wallet:status-change"] style A fill:#e5eeff80,stroke:#e5eeff80,color:#344054 style B fill:#e5eeff80,stroke:#e5eeff80,color:#344054 style C fill:#e5eeff80,stroke:#e5eeff80,color:#344054 style D fill:#e5eeff80,stroke:#e5eeff80,color:#344054 ``` \* For fiat wallets only. For crypto wallets, start with step 2: [Create a wallet](#create-a-wallet). ## Retrieve wallet profiles :::warning Required only for fiat wallets. When creating crypto wallets, start with the [Create a wallet](#create-a-wallet) step. ::: Before adding a wallet to your account, you must assign a wallet profile to it. The wallet profile is a template that defines the currency codes and payment methods available for a wallet. To list the existing wallet profiles, send the [`GET /ledger/v2/wallets/profiles`](../../api-explorer/endpoints/wallet-profiles-2/) request. Send the GET /ledger/v2/wallets/profiles request to list wallet profiles. Use the optional `q` parameter to filter results with [Lucene query syntax](https://lucene.apache.org/core/2_9_4/queryparsersyntax.html), such as `currency:USD`, `method:SWIFT`, or `customerId:550e8400-e29b-41d4-a716-446655440000 AND currency:USD`. Each profile in the response has an `id` to use as `profileId` when [creating a wallet](#create-a-wallet). Available currencies and methods vary based on your account and jurisdiction. ## Create a wallet To create a wallet, do the following: To create a fiat wallet with an [assigned wallet profile](#create-a-wallet), send the [`POST /ledger/v2/wallets`](../../api-explorer/endpoints/ledger-wallet-create-v-2) request with a JSON body like the following. Use the `id` from [`GET /ledger/v2/wallets/profiles`](../../api-explorer/endpoints/wallet-profiles-2/) as `profileId`. ```json { "currency": "USD", "name": "USD SWIFT", "profileId": "fiat:usd:141d6bae" } ``` In the successful response, you get the details of the new fiat wallet: ```json Example Response for fiat wallet { "id": "a:25060945127830:rTgK3wN:1", "name": "USD SWIFT", "status": "INACTIVE", "balance": { "amount": 0.00, "currency": "USD" }, "paymentInstruments": [], "createdAt": "2025-06-09T14:05:12.830Z", "updatedAt": "2025-06-09T14:05:12.830Z" } ``` ```json { "customerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "currency": "USD", "name": "USD ACH", "profileId": "fiat:usd:520e259f" } ``` In the successful response, you get the details of the new fiat wallet: ```json Example Response for fiat wallet { "id": "a:25060945127830:rTgK3wN:1", "name": "USD SWIFT", "customer": { // Only for customers' wallets "id": "a1f84b6c-9e23-4d17-b590-7c4a83de2f10", "name": "Meridian Healthcare" }, "status": "INACTIVE", "balance": { "amount": 0.00, "currency": "USD" }, "paymentInstruments": [], "createdAt": "2025-06-09T14:05:12.830Z", "updatedAt": "2025-06-09T14:05:12.830Z" } ``` To create a crypto wallet, send the [`POST /ledger/v2/wallets`](../../api-explorer/endpoints/ledger-wallet-create-v-2) request with a JSON body like the following. ```json { "currency": "USDT", "name": "USDT Treasury" } ``` In the successful response, you get the details of the new crypto wallet: ```json Example Response for crypto wallet { "id": "a:25060945183207:vLpR8mC:1", "name": "USDT Treasury", "status": "INACTIVE", "balance": { "amount": 0.000000, "currency": "USDT" }, "paymentInstruments": [], "createdAt": "2025-06-09T14:06:23.207Z", "updatedAt": "2025-06-09T14:06:23.207Z" } ``` ```json { "customerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "currency": "USDT", "name": "USDT Payments" } ``` In the successful response, you get the details of the new crypto wallet: ```json Example Response for crypto wallet { "id": "a:25060945183207:vLpR8mC:1", "name": "USDT Treasury", "customer": { // Only for customers' wallets "id": "c7a91d3e-48f2-4b76-a835-2e6d09f1c4b8", "name": "Greenfield Logistics" }, "status": "INACTIVE", "balance": { "amount": 0.000000, "currency": "USDT" }, "paymentInstruments": [], "createdAt": "2025-06-09T14:06:23.207Z", "updatedAt": "2025-06-09T14:06:23.207Z" } ``` The `id` of the created wallet is returned in a `Location` response header, for example `https://api.sandbox.bvnk.com/ledger/v2/wallets/{id}`. Wallets are not created immediately: a new wallet often appears as `INACTIVE` first, and then automatically transitions to an `ACTIVE` state. The activation process can take several minutes. Poll [`GET /ledger/v2/wallets/{id}`](../../api-explorer/endpoints/ledger-wallet-read-v-2) or use the webhooks rather than creating duplicate wallets if activation is slow. When the wallet is created, the `paymentInstruments` array is empty and the `balance` is `0`. The `paymentInstruments` array will be populated with the payment details after the wallet is activated and the [webhook is received](#receive-status-webhook). The wallet can also have the `TERMINATED` status. This means the wallet can no longer be used, and that state is final; BVNK can never reactivate it. All operations, including receiving pay-ins, sending payouts, and currency conversions, are blocked for the terminated wallets. You can only access its transaction history. To learn how to terminate a wallet, see the [Manage wallets](../manage-wallet) guide. 1. On the BVNK Portal, go to **Wallets** and click **Add Wallet**. ![](/img/bvnk/get-started/add-wallet-button.png) 2. Select the currency, insert a unique name for this wallet and click **Create Wallet**. ![](/img/bvnk/get-started/create-wallet-form.png) The newly created wallet appears on the portal. If you click on the wallet, you can see the actions you can take on it. ![](/img/bvnk/get-started/wallet-created-list.png) :::info When you get portal access to your production BVNK account, fiat wallets will be automatically assigned to you during onboarding. ::: ## Manage wallets To manage the wallet lifecycle, use [`POST /ledger/v2/wallets/{walletId}/actions`](../../api-explorer/endpoints/ledger-wallet-execute-action). You can perform the following actions: - `TERMINATE`: Permanently terminates the wallet. This action is irreversible. The wallet balance must be **zero** (0) before termination. For fiat wallets, the associated Virtual Account is closed. Crypto wallets do not use this mechanism. - `BLOCK`: Temporarily blocks the wallet and prevents activity. - `UNBLOCK`: Removes a previously applied block from the wallet. In the payload, specify the `action` value and provide a non-empty `comment` explaining the reason for the action. You can add the following comments from the list: - `COMPLIANCE` - `OPERATION` - `OFFBOARDING` - `CLIENT_REQUEST` - `OTHER` ```json Request { "action": "BLOCK", "comment": "COMPLIANCE" } ``` ```json Request { "action": "UNBLOCK", "comment": "COMPLIANCE" } ``` ```json Request { "action": "TERMINATE", "comment": "CLIENT_REQUEST" } ``` If the request is successful, the endpoint returns `204 No Content`. Possible error scenarios include: - `400 Bad Request`: For example, this occurs if you attempt to run `TERMINATE` when the wallet has a non-zero balance. - `409 Conflict`: For example, this occurs if you attempt to run `BLOCK` or `UNBLOCK` on a wallet that is already terminated. ## Retrieve wallets Use the wallet endpoints to monitor status and balances. `GET /ledger/v2/wallets` returns summaries only and does not include `paymentInstruments`. Full payment instrument and account details are returned by `GET /ledger/v2/wallets/{id}` for a single wallet. :::note Balance decimal precision Fiat wallet balances use **2 decimal places** (e.g. `48250.75`), while crypto wallet balances use **6 decimal places** (e.g. `14727.556775`). ::: ### All wallets To list all wallets, send the [`GET /ledger/v2/wallets`](../../api-explorer/endpoints/ledger-wallet-list-v-2) request. The list response returns wallet summaries without `paymentInstruments`. For payment instruments and full account details, call [`GET /ledger/v2/wallets/{id}`](../../api-explorer/endpoints/ledger-wallet-read-v-2) with that wallet's `{id}`. ### Specific wallet To retrieve wallet details by its unique identifier, send the [`GET /ledger/v2/wallets/{id}`](../../api-explorer/endpoints/ledger-wallet-read-v-2) request with the `{id}` specified in the path. In the response, you receive full wallet details. For fiat wallets, the `paymentInstruments` array contains the bank account details. For crypto wallets, `paymentInstruments` is an empty array. To receive crypto deposits into the wallet, generate a blockchain address using [Payment Links](../../use-cases/stablecoin-payments-for-platforms/get-payment) or [Channels](../../use-cases/stablecoin-payments-for-platforms/create-channel). ### Customer's wallets To retrieve wallets for a specific customer, send the [`GET /ledger/v2/wallets`](../../api-explorer/endpoints/ledger-wallet-list-v-2) request and filter by customer ID using `q` and the Lucene syntax, for example: ```http GET /ledger/v2/wallets?q=customerId:550e8400-e29b-41d4-a716-446655440000 AND status:ACTIVE ``` The above request returns all **active** wallets for your customer with ID `550e8400-e29b-41d4-a716-446655440000`. The response uses the same paginated format as the [All wallets](#all-wallets) list, filtered to that customer's wallets only. ## Receive status webhook To get notified each time a wallet's status changes, listen to the [wallet notification](../../api-explorer/bvnk-webhooks/ledger-wallet-status-change-v-2). --- **What's next?** Now that you have created a wallet, you can start sending and receiving payments. - See the available [use cases](find-your-usecase.mdx) to select the one that best suits your needs. - Configure a designated wallet for [customer fees](charge-customer-fees.mdx). --- ## Create an internal transfer (v.3) :::warning This is a new Internal Transfers flow, which is recommended for all new integrations. If you already have an existing integration with the previous [Transfers API](/bvnk/api-explorer/endpoints/transfer-create-v-2), you can continue using it. For the Transfers v.2 guide, see [Create an internal transfer](../create-internal-transfer). ::: Use this guide to facilitate internal transfers between wallets, with optional currency conversion. You can transfer both fiat and crypto funds. The transfer can be initiated according to the following scenarios: - Between you and your customer - Between your customers - Between wallets with different currencies (conversion transfer) :::note Right now, transferring between different BVNK legal entities, if you and your customer are onboarded to different ones, is not available. This feature is coming soon. ::: ## Transfer flows The flow depends on whether the wallets hold the same currency and, for conversion transfers, which conversion type you choose. Select a tab to see the shape of each flow before working through the steps below. A firm rate is presented, then accepted with the `digest` before the transfer settles. ```mermaid %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#EBF1FE', 'primaryBorderColor': '#3071F2', 'lineColor': '#3071F2', 'primaryTextColor': '#344054', 'actorBkg': '#EBF1FE', 'actorBorder': '#3071F2', 'actorTextColor': '#344054', 'signalColor': '#3071F2', 'signalTextColor': '#344054', 'noteBkgColor': '#EBF1FE', 'noteBorderColor': '#3071F2', 'activationBkgColor': '#D4E3FD', 'activationBorderColor': '#3071F2', 'fontSize': '15px'}, 'sequence': {'actorMargin': 80}}}%% sequenceDiagram participant Client as Your integration participant API as BVNK API Client->>API: POST /payment/v3/transfers (conversion.type: QUOTE) API-->>Client: 201 status: PROCESSING → PENDING_QUOTE_ACCEPTANCE
+ conversion.digest, conversion.expiresAt Client->>API: POST /payment/v3/transfers/{id}/actions
(ACCEPT_QUOTE + digest) API-->>Client: status: PROCESSING → COMPLETED alt Poll for status Client->>API: GET /payment/v3/transfers/{id} API-->>Client: status: COMPLETED else Or receive webhook API-)Client: payment:v3:transfer:status-change end ``` See [Conversion transfer with quote](#conversion-transfer-with-quote) for the steps. Executes immediately at market rate with no acceptance step. Fixed-debit only. ```mermaid %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#EBF1FE', 'primaryBorderColor': '#3071F2', 'lineColor': '#3071F2', 'primaryTextColor': '#344054', 'actorBkg': '#EBF1FE', 'actorBorder': '#3071F2', 'actorTextColor': '#344054', 'signalColor': '#3071F2', 'signalTextColor': '#344054', 'noteBkgColor': '#EBF1FE', 'noteBorderColor': '#3071F2', 'activationBkgColor': '#D4E3FD', 'activationBorderColor': '#3071F2', 'fontSize': '15px'}, 'sequence': {'actorMargin': 80}}}%% sequenceDiagram participant Client as Your integration participant API as BVNK API Client->>API: POST /payment/v3/transfers (conversion.type: AUTO_CONVERSION) API-->>Client: 201 status: PROCESSING → COMPLETED Note over Client,API: No acceptance step required ``` See [Conversion transfer with auto conversion](#conversion-transfer-with-auto-conversion) for the steps. When both wallets hold the same currency, omit the `conversion` object. The transfer settles directly. ```mermaid %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#EBF1FE', 'primaryBorderColor': '#3071F2', 'lineColor': '#3071F2', 'primaryTextColor': '#344054', 'actorBkg': '#EBF1FE', 'actorBorder': '#3071F2', 'actorTextColor': '#344054', 'signalColor': '#3071F2', 'signalTextColor': '#344054', 'noteBkgColor': '#EBF1FE', 'noteBorderColor': '#3071F2', 'activationBkgColor': '#D4E3FD', 'activationBorderColor': '#3071F2', 'fontSize': '15px'}, 'sequence': {'actorMargin': 80}}}%% sequenceDiagram participant Client as Your integration participant API as BVNK API Client->>API: POST /payment/v3/transfers (no conversion field) API-->>Client: 201 status: PROCESSING → COMPLETED ``` See [Same-asset transfer](#same-asset-transfer) for the steps. ## Same-asset transfer For transfers where the originator and beneficiary wallets hold the same currency, omit the `conversion` object. Create a transfer by sending the [`POST /payment/v3/transfers`](/bvnk/api-explorer/endpoints/transfer-create-v-3) request: Most transfers complete immediately, and you receive a response with the status `COMPLETED`. However, in certain cases, such as transfers between two different customers' wallets, processes may be asynchronous and not complete right away. ```json Response { "id": "d4a904e0-b040-4132-a6d3-ed7d03cdfce7", "reference": "treasury-789", "type": "payment:transfer", "status": "COMPLETED", "metadata": { "memberId": "987654321" }, "fees": { "processingFee": { "amount": 0, "currency": "USD" } }, "originator": { "amount": 100.00, "currency": "USD", "walletId": "a:25072267284363:kd3mRMO:1", "entity": { "legalName": "Acme Corp", "type": "COMPANY" } }, "beneficiary": { "amount": 100.00, "currency": "USD", "walletId": "a:25072267284363:kd3mRMO:2", "entity": { "legalName": "Globex Trading", "type": "COMPANY" } }, "expiresAt": "2025-07-31T10:00:00Z", "createdAt": "2025-07-30T10:00:00Z", "updatedAt": "2025-07-30T10:00:00Z", "trackingDetails": [], "method": "BOOK" } ``` ## Conversion transfer with quote Use conversion type `QUOTE` when you want to present a firm rate to your customer for acceptance before the transfer settles. ### Create the transfer Send the [`POST /payment/v3/transfers`](/bvnk/api-explorer/endpoints/transfer-create-v-3) request with the `conversion` object: ```json Request { "reference": "treasury-123", "originator": { "amount": 10.5, "currency": "USD", "walletId": "a:25072267284363:kd3mRMO:1" }, "beneficiary": { "currency": "EUR", "walletId": "a:25072267284363:kd3mRMO:2" }, "conversion": { "type": "QUOTE", "validityDuration": "PT20S" }, "metadata": {} } ``` The `validityDuration` field accepts `PT20S` (20 seconds) or `PT60M` (60 minutes). The transfer is created in `PENDING_QUOTE_ACCEPTANCE` status with the quoted rate: ```json Response { "id": "d4a904e0-b040-4132-a6d3-ed7d03cdfce7", "reference": "treasury-123", "type": "payment:transfer", "status": "PENDING_QUOTE_ACCEPTANCE", "metadata": {}, "fees": { "processingFee": { "amount": 0.1, "currency": "USD" } }, "originator": { "amount": 10.5, "currency": "USD", "walletId": "a:25072267284363:kd3mRMO:1", "entity": { "legalName": "Acme Corp", "type": "COMPANY" } }, "beneficiary": { "amount": 9.52, "currency": "EUR", "walletId": "a:25072267284363:kd3mRMO:2", "entity": { "legalName": "Globex Trading", "type": "COMPANY" } }, "conversion": { "type": "QUOTE", "spread": { "percentage": 0.01, "amount": 0.105, "currency": "USD" }, "allInRate": 0.9066, "baseRate": 0.8976, "validityDuration": "PT20S", "expiresAt": "2025-07-30T10:30:20Z", "digest": "a1b2c3d4e5f6" }, "expiresAt": "2025-07-31T10:00:00Z", "createdAt": "2025-07-30T10:00:00Z", "updatedAt": "2025-07-30T10:00:00Z", "trackingDetails": [], "method": "BOOK" } ``` ### Accept the quote Before the quote expires, accept it by sending the [`POST /payment/v3/transfers/{transferId}/actions`](/bvnk/api-explorer/endpoints/transfer-action-v-3) request with the `digest` value from the response: ```json Request { "type": "ACCEPT_QUOTE", "digest": "a1b2c3d4e5f6" } ``` After acceptance, the transfer moves to `PROCESSING` and settles the beneficiary amount without trading the originator's funds on a venue first. ### Refresh an expired quote If the quote expires before acceptance, you can refresh it: ```json Request { "type": "REFRESH_QUOTE" } ``` This returns the transfer with a new quote, updated `expiresAt`, and a new `digest` value. :::info All unaccepted quote transfers expire 24 hours after creation (the transfer's `expiresAt` field), regardless of individual quote expiration times. ::: ## Conversion transfer with auto conversion Use conversion type `AUTO_CONVERSION` when you want the transfer to execute automatically at the market rate without requiring quote acceptance. Send the [`POST /payment/v3/transfers`](/bvnk/api-explorer/endpoints/transfer-create-v-3) request: ```json Request { "reference": "treasury-456", "originator": { "amount": 10.5, "currency": "USD", "walletId": "a:25072267284363:kd3mRMO:1" }, "beneficiary": { "currency": "EUR", "walletId": "a:25072267284363:kd3mRMO:2" }, "conversion": { "type": "AUTO_CONVERSION" }, "metadata": {} } ``` The transfer moves to `PROCESSING` immediately. The beneficiary amount is settled after the originator's amount is traded on venue: ```json Response { "id": "d4a904e0-b040-4132-a6d3-ed7d03cdfce7", "reference": "treasury-456", "type": "payment:transfer", "status": "PROCESSING", "metadata": {}, "fees": { "processingFee": { "amount": 0.1, "currency": "USD" } }, "originator": { "amount": 10.5, "currency": "USD", "walletId": "a:25072267284363:kd3mRMO:1", "entity": { "legalName": "Acme Corp", "type": "COMPANY" } }, "beneficiary": { "amount": 9.52, "currency": "EUR", "walletId": "a:25072267284363:kd3mRMO:2", "entity": { "legalName": "Globex Trading", "type": "COMPANY" } }, "conversion": { "type": "AUTO_CONVERSION", "spread": { "percentage": 0.01, "amount": 0.105, "currency": "USD" }, "allInRate": 0.9066, "baseRate": 0.8976 }, "expiresAt": "2025-07-31T10:00:00Z", "createdAt": "2025-07-30T10:00:00Z", "updatedAt": "2025-07-30T10:00:00Z", "trackingDetails": [], "method": "BOOK" } ``` :::note Only fixed debit (amount specified on the originator side) is supported for `AUTO_CONVERSION` transfers. ::: ## Check transfer status To check the status of a transfer, send the [`GET /payment/v3/transfers/{transferId}`](/bvnk/api-explorer/endpoints/transfer-read-v-3) request. A transfer can have following statuses: | Status | Description | |--------|-------------| | `PROCESSING` | Transfer is being processed | | `PENDING_QUOTE_ACCEPTANCE` | Awaiting quote acceptance (QUOTE type only) | | `COMPLETED` | Transfer completed successfully | | `EXPIRED` | Transfer expired before quote was accepted | | `FAILED` | Transfer failed | ## Transfer webhook You can listen to the transfer webhook to get notified when the status of the transfer changes. See the [Transfer webhook](/bvnk/api-explorer/bvnk-webhooks/payment-transfer-status-change-v-3/) documentation for more details. --- ## Create an internal transfer (Legacy) This endpoint is used to facilitate internal transfers. You can transfer both fiat and crypto funds. The transfer can be initiated according to the following scenarios: - Between a partner and a customer - Between customers of the same partner :::note This capability is only available as part of the [Virtual Account use cases](/bvnk/use-cases/virtual-accounts/va-overview) and cannot be used separately. ::: ## Initiate a transfer 1. Create a transfer by sending the [`POST /payment/v2/transfers`](/bvnk/api-explorer/endpoints/transfer-create-v-2) request with the following body parameters: ```json Request { "reference": "REF558628", "walletId": "a:25032550863140:zKwR3P9:1", "amount": 111, "currency": "USD", "beneficiary": { "walletId": "a:25021926815866:4jlPfFg:1", }, "metadata": { "memberId": "987654321" } } ``` For the detailed description of fields, see the API Reference. 2. To check the status of the transfer and know when it's completed successfully or failed, you can * Listen to the transfer webhook. * Send the [`GET /payment/v2/transfers/{transferId}`](/bvnk/api-explorer/endpoints/transfer-read-v-2) request. In the successful response, you receive the details on the transfer and its status. ```json Response { "id": "ae29acc3-c54a-11f0-90b6-21c3f364ff25", "reference": "REF558628", "status": "COMPLETED", "type": "payment:transfer", "method": "BOOK", "fees": { "processingFee": { "amount": 0, "currency": "USD" } }, "originator": { "amount": 1.11, "currency": "USD", "entity": { "legalName": "3Com", "type": "COMPANY" }, "walletId": "a:25032550863140:zKwR3P9:1" }, "beneficiary": { "amount": 1.11, "currency": "USD", "entity": { "legalName": "4COM", "type": "COMPANY" }, "walletId": "a:25021926815866:4jlPfFg:1" }, "metadata": { "memberId": "987654321" }, "createdAt": "2025-11-19T13:21:38.870144Z", "updatedAt": "2025-11-19T13:21:38.870144Z", } ``` ## Transfer webhook You can listen to this webhook to get notified when the status of the transfer changes. See the [Transfer webhook](/bvnk/api-explorer/bvnk-webhooks/payment-transfer-status-change-v-2/) documentation for more details. --- ## Migrate from the Quote API to Transfers v.3 The Transfers v.3 API replaces the legacy Quote API for wallet-to-wallet currency conversion. Instead of creating a standalone quote and accepting it through a redirect flow, currency conversion is now a property of a transfer. Migrating to v.3 provides a single-field status model, digest-based acceptance that ties a client to the exact rate shown, mandatory idempotency, and the option to replace polling with a webhook. This guide walks you through the required changes for a successful migration. The core conceptual shift is that a quote is no longer a separate entity you convert. You create a **transfer**, and if the two wallets hold different currencies, you attach a **conversion** to it. ## Endpoint mapping | Operation | Legacy Quote API | Transfers v.3 | | :--- | :--- | :--- | | Create | [`POST /api/v1/quote`](/bvnk/api-explorer/endpoints/quote-create) | [`POST /payment/v3/transfers`](/bvnk/api-explorer/endpoints/transfer-create-v-3) | | Preview (estimate) | [`POST /api/v1/quote?estimate=true`](/bvnk/api-explorer/endpoints/quote-create) | [`POST /payment/v3/transfers/dry-run`](/bvnk/api-explorer/endpoints/transfer-dry-run-v-3) | | Accept | [`PUT /api/v1/quote/accept/{uuid}`](/bvnk/api-explorer/endpoints/quote-accept) | [`POST /payment/v3/transfers/{transferId}/actions`](/bvnk/api-explorer/endpoints/transfer-action-v-3) | | Get | [`GET /api/v1/quote/{uuid}`](/bvnk/api-explorer/endpoints/quote-read) | [`GET /payment/v3/transfers/{transferId}`](/bvnk/api-explorer/endpoints/transfer-read-v-3) | :::tip Historical quotes remain accessible The Quote API endpoints stay available for retrieving quotes created before you migrate. Query them with [`GET /api/v1/quote/{uuid}`](/bvnk/api-explorer/endpoints/quote-read) as before. New conversions should be created through Transfers v.3. ::: ## Flow comparison The legacy flow was always create, accept, then poll two status fields. In v.3, the flow depends on the conversion type you choose, and status is a single field. Select a tab to compare the shapes before mapping fields. Create, accept, then poll `paymentStatus` until the conversion settles. ```mermaid %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#EBF1FE', 'primaryBorderColor': '#3071F2', 'lineColor': '#3071F2', 'primaryTextColor': '#344054', 'actorBkg': '#EBF1FE', 'actorBorder': '#3071F2', 'actorTextColor': '#344054', 'signalColor': '#3071F2', 'signalTextColor': '#344054', 'noteBkgColor': '#EBF1FE', 'noteBorderColor': '#3071F2', 'activationBkgColor': '#D4E3FD', 'activationBorderColor': '#3071F2', 'fontSize': '15px'}, 'sequence': {'actorMargin': 80}}}%% sequenceDiagram participant Client as Your integration participant API as BVNK API Client->>API: POST /api/v1/quote API-->>Client: quoteStatus: PENDING Client->>API: PUT /api/v1/quote/accept/{uuid} API-->>Client: quoteStatus: PAYMENT_OUT_PROCESSED loop Poll until settled Client->>API: GET /api/v1/quote/{uuid} API-->>Client: paymentStatus: SUCCESS (or FAILED) end ``` The closest analogue to the legacy flow: a firm rate is presented, then accepted with the `digest`. ```mermaid %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#EBF1FE', 'primaryBorderColor': '#3071F2', 'lineColor': '#3071F2', 'primaryTextColor': '#344054', 'actorBkg': '#EBF1FE', 'actorBorder': '#3071F2', 'actorTextColor': '#344054', 'signalColor': '#3071F2', 'signalTextColor': '#344054', 'noteBkgColor': '#EBF1FE', 'noteBorderColor': '#3071F2', 'activationBkgColor': '#D4E3FD', 'activationBorderColor': '#3071F2', 'fontSize': '15px'}, 'sequence': {'actorMargin': 80}}}%% sequenceDiagram participant Client as Your integration participant API as BVNK API Client->>API: POST /payment/v3/transfers (conversion.type: QUOTE) API-->>Client: 201 status: PROCESSING → PENDING_QUOTE_ACCEPTANCE
+ conversion.digest, conversion.expiresAt Client->>API: POST /payment/v3/transfers/{id}/actions
(ACCEPT_QUOTE + digest) API-->>Client: status: PROCESSING → COMPLETED alt Poll for status Client->>API: GET /payment/v3/transfers/{id} API-->>Client: status: COMPLETED else Or receive webhook API-)Client: payment:v3:transfer:status-change end ``` Executes immediately at market rate with no acceptance step. Fixed-debit only. ```mermaid %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#EBF1FE', 'primaryBorderColor': '#3071F2', 'lineColor': '#3071F2', 'primaryTextColor': '#344054', 'actorBkg': '#EBF1FE', 'actorBorder': '#3071F2', 'actorTextColor': '#344054', 'signalColor': '#3071F2', 'signalTextColor': '#344054', 'noteBkgColor': '#EBF1FE', 'noteBorderColor': '#3071F2', 'activationBkgColor': '#D4E3FD', 'activationBorderColor': '#3071F2', 'fontSize': '15px'}, 'sequence': {'actorMargin': 80}}}%% sequenceDiagram participant Client as Your integration participant API as BVNK API Client->>API: POST /payment/v3/transfers (conversion.type: AUTO_CONVERSION) API-->>Client: 201 status: PROCESSING → COMPLETED Note over Client,API: No acceptance step required ``` When both wallets hold the same currency, omit the `conversion` object. The transfer settles directly. ```mermaid %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#EBF1FE', 'primaryBorderColor': '#3071F2', 'lineColor': '#3071F2', 'primaryTextColor': '#344054', 'actorBkg': '#EBF1FE', 'actorBorder': '#3071F2', 'actorTextColor': '#344054', 'signalColor': '#3071F2', 'signalTextColor': '#344054', 'noteBkgColor': '#EBF1FE', 'noteBorderColor': '#3071F2', 'activationBkgColor': '#D4E3FD', 'activationBorderColor': '#3071F2', 'fontSize': '15px'}, 'sequence': {'actorMargin': 80}}}%% sequenceDiagram participant Client as Your integration participant API as BVNK API Client->>API: POST /payment/v3/transfers (no conversion field) API-->>Client: 201 status: PROCESSING → COMPLETED ``` ## What changed ### Create quote The legacy request described a conversion between two wallets with a flat set of fields. In v.3, the request describes a transfer with an `originator` and a `beneficiary`, and conversion is an optional nested object. Every v.3 create also requires an `Idempotency-Key` header. ```json title="Request: POST /api/v1/quote" { "from": "EUR", "to": "USDC", "fromWalletLsid": "a:25052137356562:qjOSsJf:1", "toWalletLsid": "a:24092328494070:G5i4XZ9:2", "amountIn": 500, "useMinimum": false, "useMaximum": false, "payInMethod": "wallet", "payOutMethod": "wallet" } ``` ```json title="Request: POST /payment/v3/transfers (header Idempotency-Key: 1e74002e-74a1-48fd-b707-147c3187a3e1)" { "reference": "treasury-123", "originator": { "amount": 500, "currency": "EUR", "walletId": "a:25052137356562:qjOSsJf:1" }, "beneficiary": { "currency": "USDC", "walletId": "a:24092328494070:G5i4XZ9:2" }, "conversion": { "type": "QUOTE", "validityDuration": "PT20S" } } ``` **Field changes:** | Legacy field | v.3 field | Notes | | :--- | :--- | :--- | | `from` | `originator.currency` | Currency to convert from. | | `to` | `beneficiary.currency` | Currency to convert to. | | `fromWalletLsid` | `originator.walletId` | Wallet being debited. | | `toWalletLsid` | `beneficiary.walletId` | Wallet being credited. | | `amountIn` | `originator.amount` | Fixed-debit. With `QUOTE` you may instead set `beneficiary.amount` for a fixed-credit conversion — specify exactly one side. | | `useMinimum`, `useMaximum` | — | Removed. | | `payInMethod`, `payOutMethod` | — | Removed. v3 transfers are always `BOOK` (internal wallet) transfers. | | — | `reference` | Now required. A unique reference for the transfer. | | — | `conversion.type` | `QUOTE` or `AUTO_CONVERSION`. Omit the whole `conversion` object for same-currency transfers. | | — | `conversion.validityDuration` | Optional. `PT20S` or `PT60M`. Applies to `QUOTE` only. | | — | `metadata` | Optional key-value map for your own tracking data. | | — | `Idempotency-Key` header | **Required.** A client-generated UUID, reused on retries. | :::note Estimate mode on v.3 The legacy `?estimate=true` query parameter maps to a dedicated preview endpoint on v.3: [`POST /payment/v3/transfers/dry-run`](/bvnk/api-explorer/endpoints/transfer-dry-run-v-3). It returns the rate, spread, fees, and converted amounts without persisting a transfer or moving funds. See [Preview a transfer](../create-internal-transfer-v3#preview-a-transfer). ::: ### Choose a conversion type The legacy API had one conversion path. v.3 gives you three, chosen by the `conversion` object: - **`QUOTE`** presents a firm rate that your customer must accept before the transfer settles. This is the closest analogue to the legacy create-then-accept flow. - **`AUTO_CONVERSION`** executes immediately at the prevailing market rate with no acceptance step. Only fixed-debit is supported (set `originator.amount`). - **Same-currency** when both wallets hold the same currency, omit the `conversion` object entirely and the transfer settles directly. For the full request and response shapes of each type, see [Create an internal transfer (v.3)](../create-internal-transfer-v3). ### Accept the quote In the legacy API, acceptance was a redirect-oriented step: you sent a `successUrl` and the conversion executed. In v3, you accept by sending an `ACCEPT_QUOTE` action carrying the `digest` from the Create endpoint's response. The digest is a fingerprint of the quoted rate. Passing it back proves the client agreed to the specific rate shown. There is no redirect. ```json title="Request: PUT /api/v1/quote/accept/{uuid}" { "successUrl": "https://app.sandbox.bvnk.com/wallets/convert/complete/{{quote_id}}" } ``` ```json title="Request: POST /payment/v3/transfers/{transferId}/actions" { "type": "ACCEPT_QUOTE", "digest": "a1b2c3d4e5f6" } ``` If the quote expires before you accept it, send a `REFRESH_QUOTE` action to obtain a new rate and `digest`, then accept: ```json title="Request: POST /payment/v3/transfers/{transferId}/actions" { "type": "REFRESH_QUOTE" } ``` ### Status model The legacy API required you to interpret two fields, `quoteStatus` and `paymentStatus`, to determine an outcome. v.3 consolidates these into a single `status` field, which is the source of truth. | Outcome | Legacy `quoteStatus` / `paymentStatus` | v.3 `status` | | :--- | :--- | :--- | | Just created (non-quote) | `PENDING` / `PENDING` | `PROCESSING` | | Awaiting quote acceptance | `PENDING` / `PENDING` | `PENDING_QUOTE_ACCEPTANCE` | | Accepted, settling | `ACCEPTED` / `PENDING` | `PROCESSING` | | Completed | `PAYMENT_OUT_PROCESSED` / `SUCCESS` | `COMPLETED` | | Conversion failed | `CONVERSION_FAILED` / `FAILED` | `FAILED` | | Pay-out failed | `PAYMENT_OUT_FAILED` / `FAILED` | `FAILED` | | Pay-in failed | `PAYMENT_IN_FAILED` / `FAILED` | `FAILED` | | Quote validity elapsed | — | `EXPIRED` | :::caution EXPIRED is a distinct terminal state In the legacy API, an unaccepted quote surfaced as a failure. In v.3, a transfer whose quote is not accepted in time transitions to `EXPIRED`, separate from `FAILED`. Update any logic that treats `FAILED` as the only non-success terminal state. ::: ### Response and conversion details The response is now nested by participant, timestamps are ISO-8601 instead of Unix epoch, and rate and spread details live under the `conversion` object. | Legacy field | v.3 field | Notes | | :--- | :--- | :--- | | `uuid` | `id` | UUID identifier of the transfer. | | `amountIn` | `originator.amount` | Now nested. | | `amountOut` | `beneficiary.amount` | Now nested. | | `from` | `originator.currency` | Plain currency code in both. | | `to` | `beneficiary.currency` | Plain currency code in both. | | `quoteStatus` + `paymentStatus` | `status` | Merged into a single field. | | `price` | `conversion.allInRate` | Effective rate including spread. | | `fee`, `fees` | `fees.processingFee` | BVNK processing fee applied to the debiting wallet. | | `acceptanceExpiryDate` | `conversion.expiresAt` | When the quoted rate expires. Now ISO-8601 instead of epoch milliseconds. See the note below on the two expiry fields. | | `dateCreated` | `createdAt` | Now ISO-8601. | | `lastUpdated` | `updatedAt` | Now ISO-8601. | | `payInMethod`, `payOutMethod` | `method: "BOOK"` | Always `BOOK` for Transfers v.3. | | — | `conversion.baseRate` | New. Mid-market rate before spread. | | — | `conversion.spread` | New. Nested `percentage`, `amount`, and `currency`. | | — | `conversion.digest` | New. Required to accept the quote. | :::note Two expiry fields in v.3 The legacy `acceptanceExpiryDate` maps to `conversion.expiresAt`, not the top-level `expiresAt`. v.3 exposes both: - `conversion.expiresAt` — when the **quoted rate** expires. Accept the quote before this time, or refresh it with a `REFRESH_QUOTE` action. - `expiresAt` (top level) — when the **transfer itself** expires. Unaccepted quote transfers expire 24 hours after creation, regardless of the shorter quote validity window. ::: A `QUOTE` create response returns the conversion details you need for acceptance: ```json title="Response: POST /payment/v3/transfers" { "id": "d4a904e0-b040-4132-a6d3-ed7d03cdfce7", "reference": "treasury-123", "type": "payment:transfer", "status": "PENDING_QUOTE_ACCEPTANCE", "fees": { "processingFee": { "amount": 0.5, "currency": "EUR" } }, "originator": { "amount": 500, "currency": "EUR", "walletId": "a:25052137356562:qjOSsJf:1" }, "beneficiary": { "amount": 537.10, "currency": "USDC", "walletId": "a:24092328494070:G5i4XZ9:2" }, "conversion": { "type": "QUOTE", "spread": { "percentage": 0.01, "amount": 5.00, "currency": "EUR" }, "allInRate": 1.0742, "baseRate": 1.0850, "validityDuration": "PT20S", "expiresAt": "2025-07-30T10:30:20Z", "digest": "a1b2c3d4e5f6" }, "expiresAt": "2025-07-31T10:00:00Z", "createdAt": "2025-07-30T10:00:00Z", "updatedAt": "2025-07-30T10:00:00Z", "method": "BOOK" } ``` The `conversion` object is present in the response only for cross-currency transfers. Its fields, and the legacy quote fields they replace, are: | v.3 `conversion` field | Replaces (legacy) | Description | | :--- | :--- | :--- | | `conversion.type` | — | `QUOTE` or `AUTO_CONVERSION`. | | `conversion.allInRate` | `price` | Effective rate including spread (originator → beneficiary). | | `conversion.baseRate` | — | Base market rate before spread. | | `conversion.spread.percentage` | — | Spread as a decimal fraction (for example, `0.01` = 1%). | | `conversion.spread.amount` | — | Spread amount in the spread currency. | | `conversion.spread.currency` | — | Currency of the spread amount. | | `conversion.validityDuration` | — | Quote validity window. `QUOTE` only. | | `conversion.expiresAt` | `acceptanceExpiryDate` | When the quoted rate expires. `QUOTE` only. | | `conversion.digest` | — | Opaque value passed back in the `ACCEPT_QUOTE` action. `QUOTE` only. | The legacy `fee` field is not part of the conversion object in v.3; the BVNK processing fee is reported separately under `fees.processingFee`. ### Notifications (new in v.3) The legacy quote flow had no outbound webhooks. You polled [`GET /api/v1/quote/{uuid}`](/bvnk/api-explorer/endpoints/quote-read) for status updates. Transfers v.3 supports the [Transfer webhook](/bvnk/api-explorer/bvnk-webhooks/payment-transfer-status-change-v-3/) (`payment:v3:transfer:status-change`) as an alternative to polling. Adopting it is recommended but not mandatory; polling [`GET /payment/v3/transfers/{transferId}`](/bvnk/api-explorer/endpoints/transfer-read-v-3) remains available. The webhook fires on each status change, and its payload mirrors the [`GET /payment/v3/transfers/{transferId}`](/bvnk/api-explorer/endpoints/transfer-read-v-3) response, including the `conversion` object for cross-currency transfers. Each event carries a `direction` so you know which side of the transfer it concerns: - `OUT` is delivered to the originator of the transfer. - `IN` is delivered to the beneficiary of the transfer. When the originator and beneficiary are the same wallet, both an `IN` and an `OUT` event are delivered. ## Flow differences to be aware of 1. **Idempotency is now mandatory.** The legacy quote create had no idempotency mechanism. In v.3, every [`POST /payment/v3/transfers`](/bvnk/api-explorer/endpoints/transfer-create-v-3) call requires a unique `Idempotency-Key` header, a client-generated UUID. Reuse the same key when retrying after a network failure so the transfer is not created twice. 2. **Acceptance uses a digest, not a redirect.** The legacy [`PUT /api/v1/quote/accept/{uuid}`](/bvnk/api-explorer/endpoints/quote-accept) took a `successUrl` and completed the conversion through a redirect. In v.3, you accept by sending an `ACCEPT_QUOTE` action carrying the `digest` from the create (or GET) response. Store the `digest` and pass it back at acceptance time. This ties the acceptance to the exact rate the client was shown, with no redirect involved. 3. **Quote validity is now a fixed set of durations.** The legacy API set the acceptance window server-side with no way to request a duration. In v.3, you can optionally set `conversion.validityDuration` to `PT20S` or `PT60M`. If omitted, the server applies a default. 4. **Fixed-credit amounts are now possible, but only with `QUOTE`.** The legacy quote request accepted only a fixed debit (`amountIn`). In v.3, `AUTO_CONVERSION` remains fixed-debit only (set `originator.amount`). `QUOTE` additionally lets you fix the credit side by setting `beneficiary.amount` instead. Specify exactly one side. 5. **The estimate mode is now a dedicated dry-run endpoint.** The legacy API supported [`POST /api/v1/quote?estimate=true`](/bvnk/api-explorer/endpoints/quote-create) to get an indicative rate without persisting a quote. Send [`POST /payment/v3/transfers/dry-run`](/bvnk/api-explorer/endpoints/transfer-dry-run-v-3) instead. It takes the same request body as the create call and returns the rate, spread, fees, and converted amounts without persisting a transfer or moving funds. It works for both `QUOTE` and `AUTO_CONVERSION` conversions but not for same-currency transfers, where there is nothing to preview. See [Preview a transfer](../create-internal-transfer-v3#preview-a-transfer). 6. **The Quote API stays available for historical records.** The legacy [`GET /api/v1/quote/{uuid}`](/bvnk/api-explorer/endpoints/quote-read) is not deprecated. Quotes created through the Quote API before you migrate remain retrievable through it. Transfers created via v.3 must be queried with [`GET /payment/v3/transfers/{transferId}`](/bvnk/api-explorer/endpoints/transfer-read-v-3) instead. ## Migration checklist 1. **Update quote creation:** replace [`POST /api/v1/quote`](/bvnk/api-explorer/endpoints/quote-create) with [`POST /payment/v3/transfers`](/bvnk/api-explorer/endpoints/transfer-create-v-3). - Generate and send an `Idempotency-Key` header on every request. - Map `from`/`to` → `originator.currency`/`beneficiary.currency` and `fromWalletLsid`/`toWalletLsid` → `originator.walletId`/`beneficiary.walletId`. - Move `amountIn` to `originator.amount`; add a required `reference`. - Add `conversion: { type: "QUOTE" }` for firm-rate flows, `AUTO_CONVERSION` for immediate execution, or omit `conversion` for same-currency transfers. 2. **Update acceptance:** replace [`PUT /api/v1/quote/accept/{uuid}`](/bvnk/api-explorer/endpoints/quote-accept) with [`POST /payment/v3/transfers/{transferId}/actions`](/bvnk/api-explorer/endpoints/transfer-action-v-3). - Store `conversion.digest` from the create response and send `{ "type": "ACCEPT_QUOTE", "digest": "" }`. - Handle expired quotes by sending a `REFRESH_QUOTE` action to get a new digest, then re-accepting. 3. **Update status handling:** replace the dual `quoteStatus` + `paymentStatus` checks with the single `status` field. Treat `EXPIRED` as a distinct terminal state and `PENDING_QUOTE_ACCEPTANCE` as a non-terminal state that requires client action. 4. **Update response parsing:** read amounts from `originator.amount` / `beneficiary.amount`, the rate from `conversion.allInRate`, the fee from `fees.processingFee`, and switch timestamp parsing from Unix epoch to ISO-8601 (`createdAt`, `updatedAt`, `conversion.expiresAt`). 5. **Update status retrieval:** replace [`GET /api/v1/quote/{uuid}`](/bvnk/api-explorer/endpoints/quote-read) polling with [`GET /payment/v3/transfers/{transferId}`](/bvnk/api-explorer/endpoints/transfer-read-v-3), and optionally subscribe to the [Transfer webhook](/bvnk/api-explorer/bvnk-webhooks/payment-transfer-status-change-v-3/) to eliminate polling. 6. **Keep the Quote API for historical records:** continue using [`GET /api/v1/quote/{uuid}`](/bvnk/api-explorer/endpoints/quote-read) to retrieve quotes created before migration. --- **Related guides:** - [Create an internal transfer (v.3)](../create-internal-transfer-v3): full v.3 integration guide covering all three conversion types. --- ## Currencies BVNK supports a wide range of currencies which is always evolving. This page provides a comprehensive overview of all currently supported currencies and their specifications. ## Fiat currencies | Currency Code | Currency Name | Account Number Format (Local) | Account Number Format (SWIFT) | Use for | Decimals | | :-----------: | :------------:| :----------------:| :----------------:| :------:| :------: | | USD | US Dollar | ABA, CUBIX | Any format (e.g., IBAN) | Deposits / Payouts | 2 | | EUR | Euro | IBAN | Any format (e.g., IBAN) | Deposits / Payouts | 2 | | GBP | British Pound | IBAN, SCAN | Any format (e.g., IBAN) | Deposits / Payouts | 2 | ## Crypto currencies Crypto transactions must obtain enough confirmations on the relevant blockchain before they are considered complete. BVNK will wait for the `Required confirmations` detailed below to be reached. :::info The available cryptocurrencies depend on the specific jurisdiction. ::: Currency Code Currency Name Network Required confirmations Use for Decimals Stablecoins EURC Euro Coin ETHEREUM 12 Deposits / Payouts 6 USDC USD Coin ARBITRUM 300 Deposits / Payouts 6 USDC USD Coin BASE 10 Deposits / Payouts 6 USDC USD Coin BINANCE 4 Deposits / Payouts 6 USDC USD Coin ETHEREUM 4 Deposits / Payouts 6 USDC USD Coin POLYGON 4 Deposits / Payouts 6 USDC USD Coin SOLANA 32 Deposits / Payouts 6 USDG Global Dollar SOLANA 32 Deposits / Payouts 9 Other BNB Binance Coin BINANCE 4 Deposits / Payouts 12 BTC Bitcoin 1 Deposits / Payouts 8 ETH Ethereum 4 Deposits / Payouts 8 LTC Litecoin 4 Deposits / Payouts 8 POL Polygon POLYGON 4 Deposits / Payouts 8 SOLANA Solana 32 Deposits / Payouts 8 TRX Tronix 4 Deposits / Payouts 5 XRP Ripple When includedin validated ledger Deposits / Payouts 6 Currency Code Currency Name Network Required confirmations Use for Decimals Stablecoins EURC Euro Coin ETHEREUM 12 Deposits / Payouts 6 PYUSD PayPal USD ETHEREUM 4 Deposits / Payouts 6 USDC USD Coin ARBITRUM 300 Deposits / Payouts 6 USDC USD Coin BASE 10 Deposits / Payouts 6 USDC USD Coin BINANCE 4 Deposits / Payouts 6 USDC USD Coin ETHEREUM 4 Deposits / Payouts 6 USDC USD Coin POLYGON 4 Deposits / Payouts 6 USDC USD Coin SOLANA 32 Deposits / Payouts 6 USDG Global Dollar SOLANA 32 Deposits / Payouts 9 USDT Tether USD BINANCE 4 Deposits / Payouts 6 USDT Tether USD ETHEREUM 4 Deposits / Payouts 6 USDT Tether USD POLYGON 4 Deposits / Payouts 6 USDT Tether USD TRON 4 Deposits / Payouts 6 Other BNB Binance Coin BINANCE 4 Deposits / Payouts 12 BTC Bitcoin 1 Deposits / Payouts 8 ETH Ethereum 4 Deposits / Payouts 8 LTC Litecoin 4 Deposits / Payouts 8 POL Polygon POLYGON 4 Deposits / Payouts 8 SOLANA Solana 32 Deposits / Payouts 8 TRX Tronix 4 Deposits / Payouts 5 XRP Ripple When includedin validated ledger Deposits / Payouts 6 Currency Code Currency Name Network Required confirmations Use for Decimals Stablecoins EURC Euro Coin ETHEREUM 12 Deposits / Payouts 6 PYUSD PayPal USD ETHEREUM 12 Deposits / Payouts 6 PYUSD PayPal USD SOLANA 32 Deposits / Payouts 6 USDC USD Coin ARBITRUM 300 Deposits / Payouts 6 USDC USD Coin BASE 10 Deposits / Payouts 6 USDC USD Coin BINANCE 4 Deposits / Payouts 6 USDC USD Coin ETHEREUM 4 Deposits / Payouts 6 USDC USD Coin POLYGON 4 Deposits / Payouts 6 USDC USD Coin SOLANA 32 Deposits / Payouts 6 USDG Global Dollar SOLANA 32 Deposits / Payouts 9 USDT Tether USD BINANCE 4 Deposits / Payouts 6 USDT Tether USD ETHEREUM 4 Deposits / Payouts 6 USDT Tether USD POLYGON 4 Deposits / Payouts 6 USDT Tether USD TRON 4 Deposits / Payouts 6 Other BNB Binance Coin BINANCE 12 Deposits / Payouts 12 BTC Bitcoin 1 Deposits / Payouts 8 ETH Ethereum 4 Deposits / Payouts 8 LTC Litecoin 12 Deposits / Payouts 8 POL Polygon POLYGON 12 Deposits / Payouts 8 SOLANA Solana 32 Deposits / Payouts 8 TRX Tronix 4 Deposits / Payouts 5 XRP Ripple 1 Deposits / Payouts 6 :::info Non-MTL US States Businesses and individuals whose US state of domicile is not covered by BVNK's Money Transmitter License (MTL) have access to a reduced set of currencies for deposits and payouts, listed below. The affected states are: CA, GA, HI, LA, MA, MT, NJ, NY, TX, VA, WA, and WI. See [What licenses registrations does BVNK and BVNK's Partners have](https://help.bvnk.com/hc/en-us/articles/11094354781074-What-licenses-registrations-does-BVNK-and-BVNK-s-Partners-have) for BVNK's current state license coverage. ::: The following currencies and networks are available to businesses and individuals domiciled in a non-MTL state: * **PYUSD** (PayPal USD): Ethereum, Solana * **USDC** (USD Coin): Base, Ethereum, Polygon, Solana * **USDG** (Global Dollar): Solana * **USDT** (Tether USD): Ethereum Currency Code Currency Name Network Required confirmations Testnet Use for Decimals Stablecoins USDC USD Coin ETHEREUM 4 Sepolia Deposits / Payouts 6 USDC USD Coin POLYGON 4 Amoy Deposits / Payouts 6 USDT Tether USD ETHEREUM 4 Sepolia Deposits / Payouts 6 USDT Tether USD TRON 4 Nile Deposits / Payouts 6 Other ETH Ethereum 4 Sepolia Deposits / Payouts 8 TRX Tronix 4 Nile Deposits / Payouts 5 --- ## Date and time Date and times are encoded into UNIX epoch timestamps, including milliseconds, for example, `1566203005000` is `Monday, August 19, 2019 8:23:25 AM`. --- ## Glossary
[A](#a) • [B](#b) • [C](#c) • [D](#d) • [E](#e) • [F](#f) • [H](#h) • [I](#i) • [K](#k) • [M](#m) • [N](#n) • [O](#o) • [P](#p) • [Q](#q) • [R](#r) • [S](#s) • [T](#t) • [V](#v) • [W](#w)
## A ### Account A registered entity on the BVNK platform that manages wallets, processes payments, and oversees API credentials. In the Direct model, an account identifies the business contracting directly with BVNK. See [Select your delivery model](../../get-started/delivery-models). ### Address Also, a blockchain address or a crypto address. A unique identifier on a specific blockchain network where digital assets can be sent or received. See [Integrate Channels](../../use-cases/stablecoin-payments-for-platforms/create-channel). ### Associate A person linked to a business customer during onboarding, such as a director, ultimate beneficial owner (UBO), or authorised representative. At least one representative, UBO, and a director must be specified when onboarding a business customer. See [Business requirements](../../get-started/compliance-requirements/embedded-compliance-requirements-businesses). ## B ### Balance The amount of funds available in a wallet. A balance includes the currency code and the current value. Each wallet maintains its own balance, which is updated as transactions are processed. See [List Wallet Balances](../../api-explorer/endpoints/wallet-balance-list). ### Beneficiary The entity that receives the transaction. Can be an individual or a business. See [Verify Beneficiary's Name](../../api-explorer/endpoints/verify-beneficiary). ### Blockchain address The destination for a crypto payout. A blockchain address is a unique identifier on a specific blockchain network where digital assets are sent. See [Initiate Payout](../../api-explorer/endpoints/payout-create-v-2). ## C {/* ### Card A virtual stablecoin-backed card linked to wallet balances, enabling spend. Cards are available as part of the Embedded Express model and are issued to customers by default. See [Enable Express wallets for customers](../../use-cases/xpress-store-funds/express-overview). */} ### CDD Customer due diligence is the process of collecting and verifying customer information during onboarding. This includes employment status, source of funds, intended use of account, and politically exposed person (PEP) status. See [Individual requirements](../../get-started/compliance-requirements/compliance-requirements-for-individuals2). ### Channel A dedicated, permanent, and unique address that can receive crypto funds at any time. Opposed to a temporary address that can be used for a single transaction. See [Integrate Channels](../../use-cases/stablecoin-payments-for-platforms/create-channel) and [Create Channel](../../api-explorer/endpoints/channel-create). ### Crypto A cryptocurrency is a digital currency that uses cryptography for security. It is decentralised and operates independently of a central bank. See [Currencies](../currencies). ### Crypto gateway BVNK's crypto payment processing service that handles the creation and management of crypto payments through Payment Links. It supports pay-ins, payouts, and fiat-to-crypto and crypto-to-fiat currency conversions. See [Create Payment](../../api-explorer/endpoints/payment-create). ### Customer Previously called Embedded Partner Customer (EPC). Legacy terms also include nested or underlying customers. An individual or business that uses BVNK's services through a partner. See [Create a customer](../../get-started/create-embedded-partner-customer/creating-an-embedded-customer-company-api). ### Customer fee An optional markup that a partner adds to customer transactions. Customer fees are distinct from transaction amounts and BVNK processing fees, and are withdrawn from the customer's wallet to the partner's designated fee wallet. See [Charge customer fees](../../get-started/charge-customer-fees). ## D ### Digital asset A cryptocurrency asset or a stablecoin. See [Currencies](../currencies). ### Direct model A delivery model where BVNK has a direct relationship with a [partner](#partner). See [Select your delivery model](../../get-started/delivery-models). ## E {/* ### Embedded express A BVNK-hosted, co-branded wallet experience delivered via link or SDK. BVNK owns the regulated user flows, and partners get rapid rollout with limited customisation and no write actions on user accounts. See [Enable Express wallets for customers](../../use-cases/xpress-store-funds/express-overview). */} ### Embedded model A delivery model where BVNK embeds its services into a partner's platform. See [Select your delivery model](../../get-started/delivery-models). ### Endpoint Also called an API endpoint. A URL used to send requests to BVNK's servers. See [API overview](../../api-explorer/api-overview/overview). ## F ### Fiat A government-issued currency that is not backed by a physical commodity, such as USD, EUR, or GBP. BVNK supports fiat currencies for pay-ins, payouts, and conversions to and from crypto. See [Currencies](../currencies). ## H ### Hosted payments page Or Hosted page. A hosted checkout experience embedded in your platform interface. See [Integrate Payment Links](../../use-cases/stablecoin-payments-for-platforms/get-payment). ## I ### Idempotency A property of certain API endpoints that lets you safely retry requests without duplicating actions. To use idempotency, include a unique `X-Idempotency-Key` header (or `Idempotency-Key` for v2 endpoints) with your request. Only successful requests are cached; failed requests may be retried without risk of conflict. See [Idempotency](../../api-explorer/api-overview/idempotency). ## K ### KYC/KYB Know your customer (KYC) and know your business (KYB). Verification processes that BVNK performs to confirm the identity and legitimacy of individual and business customers during onboarding. These checks are a regulatory requirement and a critical component of the risk management process. See [Individual requirements](../../get-started/compliance-requirements/compliance-requirements-for-individuals2) and [Business requirements](../../get-started/compliance-requirements/embedded-compliance-requirements-businesses). ## M ### Mass payouts A function enabling you to upload a CSV file and trigger multiple payouts in a single batch. ### Merchant The legacy term for a partner that uses BVNK's services directly. Same as [Account](#account). See [Create Merchant ID](../../api-explorer/endpoints/merchant-id-create). ### Metadata Additional information about a transaction included in a request or response. See [Metadata](../../api-explorer/api-overview/metadata). ```json Metadata Example { "metadata": { "orderId": "PO-2025-001", "internalUserId": "user-456", "reason": "Customer withdrawal" } } ``` ## N ### Network A blockchain protocol used to process crypto transactions. BVNK supports multiple networks, including Ethereum, Tron, Solana, and Bitcoin. The network affects transaction speed, fees, and confirmation requirements. See [Currencies](../currencies). ## O ### Onboarding The process of setting up a customer to use BVNK's services integrated into the [partner](#partner)'s platform. The onboarding process typically involves a collaborative review by BVNK's Compliance and Onboarding teams to ensure full adherence to Know Your Business (KYB) standards. See [Gather compliance data for onboarding](../../get-started/create-embedded-partner-customer/onboard-epc). ### On-ramp A service that allows you to buy crypto with fiat currency. See [Convert funds (on-ramp)](../../use-cases/on-off-ramp/on-ramp-overview). ### Originator The entity that initiates the transaction. For example, a customer who wants to buy crypto with fiat currency. See [Provide additional party information](../../use-cases/stablecoin-payments-for-platforms/provide-user-information-for-payments). ## P ### Partner Formerly, an Embedded Partner or EP. Anyway, that's... You for BVNK. A Business that embeds BVNK's services into their platform for themselves or their customers. See [Add customers](../../get-started/create-embedded-partner-customer/creating-an-embedded-customer-company-api). ### Pay-in A request to move fiat funds from a customer to your wallet. See [Receive payments](../../use-cases/stablecoin-payments-for-platforms/get-payment-overview). ### Payout A request to move crypto funds from your wallet to the beneficiary. See [Send stablecoin payments](../../use-cases/stablecoin-payments-for-platforms/payout-overview) and [Initiate Payout](../../api-explorer/endpoints/payout-create-v-2). ### Payment link A link created and shared via the BVNK API to collect crypto pay‑ins and process payouts. See [Integrate Payment Links](../../use-cases/stablecoin-payments-for-platforms/get-payment) and [Create Payment](../../api-explorer/endpoints/payment-create). ### Payments API BVNK's API for integrating crypto and fiat payments into a partner's platform. The Payments API supports pay-ins, payouts, conversions, and channels. See [API overview](../../api-explorer/api-overview/overview). ### Peg Also, pegged. A fixed value relationship between a stablecoin and a fiat currency, where the stablecoin maintains a stable exchange rate relative to the fiat currency. For example, USDC is pegged to USD at a 1:1 ratio. ### Processing fee A fee charged by BVNK for processing the transaction. ## Q ### Quote A price estimate for converting currency between wallets. A quote includes the exchange rate, the amount to be converted, and an expiry time. You can create an estimate quote to preview the conversion, or create and accept a quote to execute the conversion. See [Create Quote](../../api-explorer/endpoints/quote-create). ## R {/* ### Rewards A built-in ability for wallet balances to automatically accrue yield. Rewards are enabled by default for all Embedded Express wallets and require no additional setup. Partners can configure how reward income is shared with their customers. See [Enable Express wallets for customers](../../use-cases/xpress-store-funds/express-overview). */} ### Refund A request to reverse a payment. It is initiated by the [originator](#originator) and is processed by the [beneficiary](#beneficiary). See [Refund payments](../../use-cases/virtual-accounts/refund-payments) and [Refund Pay-in](../../api-explorer/endpoints/refund-payin). ### Reference A unique identifier for a transaction. BVNK may use the following fields to reference a transaction: - `paymentReference`: Actual description of the payment. The reference text is propagated along with the transfer, so the beneficiary could see it. - `customerReference`: Unique identifier of the customer. If the underlying customer is provided, this field would contain `customerId` called `customerReference` on the BVNK side. - `transactionReference`: Unique transaction ID generated by BVNK. ## S ### Stablecoin A cryptocurrency that is pegged to a fiat currency or a commodity. For example, USDC, USDT, etc. See [Currencies](../currencies). ## T ### Transaction A record of a financial operation processed through BVNK, such as a pay-in, payout, fee, internal transfer, or channel deposit. Each transaction includes an amount, currency, status, and a unique identifier. See [Reconcile transactions](../../reconciliation/reconcile-transactions) and [List Transactions](../../api-explorer/endpoints/wallet-list-transactions). ### Transfer Also, internal transfer. A transfer between two wallets within the same account. See [Create internal transfer](../../use-cases/create-internal-transfer) and [API endpoint: Create Internal Transfer](../../api-explorer/endpoints/transfer-create). ### Travel rule A regulatory requirement under the EU's MiCA regulation that extends traditional payment information-sharing standards to crypto and stablecoin transactions. The travel rule requires collecting and sharing information about the parties involved in a transaction, such as the originator and beneficiary. See [Provide additional party information](../../use-cases/stablecoin-payments-for-platforms/provide-user-information-for-payments). ## V ### Virtual Account A BVNK-managed payment account that includes a virtual IBAN (vIBAN) and a wallet. Virtual accounts are used to send, hold, and receive funds in one place. See [Virtual accounts overview](../../use-cases/virtual-accounts/va-overview). ### Verification of Payee (VoP) VoP is a Single Euro Payments Area (SEPA) service that verifies a beneficiary's name against their IBAN to prevent fraud and errors. See [Send fiat payments](../../use-cases/virtual-accounts/initiate-payment-2#request-vop). ## W ### Wallet A container that stores value and maintains balances in either fiat or crypto. You can connect various payment instruments, such as crypto addresses, to a wallet. Wallets are the core building block for processing payments, conversions, and transfers in BVNK. See [Set up wallets](../../get-started/set-up-wallets) and [Create Wallet](../../api-explorer/endpoints/create-customer-wallet). ### Wallet profile A configuration that defines the currency codes and payment methods available for a wallet. Wallet profiles are assigned when a wallet is created and determine which payment rails the wallet can use. See [Set up wallets](../../get-started/set-up-wallets#create-a-wallet) and [List Wallet Profiles](../../api-explorer/endpoints/wallet-profiles). ### Webhook A URL that allows you to receive notifications from BVNK. See [Create webhook listener](../../get-started/create-webhook-listener) for more information. --- ## Industry references This is a definitive list of industries and sub-industries which can be included in the payload when creating a customer. ## Retrieve industry references To get the complete list of industries, send the [`GET /platform/v1/accounts/industries`](../../api-explorer/endpoints/get-industries) request. In the response, you receive the existing industry reference. ## List of references The JSON response contains the following attributes: **Parent industry category**: | Attribute | Description | | --------- | -------------------------------------------------------------------------------------- | | `name` | Name of the industry. For example, "Accommodation and food services" | | `children` | Array of sub-industries associated with the parent one. | **Children categories**: | Attribute | Description | | --------- | ------------------------------------------------------------------------------ | | `reference` | Unique identifier of the sub-industry. "30ea15fc-06d8-11ef-b6e1-027612b7f6b5". | | `name` | Name of the sub-industry. For example,"Food/Beverages". | | `naicsCode` | North American Industry Classification System code. | | `naceCodes` | Array of Statistical Classification of Economic Activities in the European Community codes. | ```json { "name": "Accommodation and food services", "children": [ { "reference": "30ea15fc-06d8-11ef-b6e1-027612b7f6b5", "name": "Food/Beverages", "naicsCode": 311, "naceCodes": [1089, 4634, 5621] }, { "reference": "30ea5818-06d8-11ef-b6e1-027612b7f6b5", "name": "Supermarkets/Convenience Store", "naicsCode": 445, "naceCodes": [4711, 4639, 4729] } ] } ``` The following table contains all industry references for both sandbox and production environments with their corresponding NACE and NAICS codes: | Name | Sandbox Reference | Production Reference | NACE Code (Related NACE 1, NACE 2, NACE 3) | NAICS Code | |------|-------------------|----------------------|-----------|------------| | **Accommodation and food services** | | | | | | Food/Beverages | 30ea15fc-06d8-11ef-b6e1-027612b7f6b5 | 56e66027-06fa-11ef-bbf8-02d3d923cf2b | 1089 (4634, 5621) | 311 | | Hospitality | 30ea17fd-06d8-11ef-b6e1-027612b7f6b5 | 56e66349-06fa-11ef-bbf8-02d3d923cf2b | 5510 (5610, 5630, 5520) | 72 | | Recreational Facilities/Services (Spas, Saunas etc) | 30ea1a02-06d8-11ef-b6e1-027612b7f6b5 | 56e66697-06fa-11ef-bbf8-02d3d923cf2b | 9329 (9313) | 7139 | | Supermarkets/Convenience Store | 30ea5818-06d8-11ef-b6e1-027612b7f6b5 | 56e66ce7-06fa-11ef-bbf8-02d3d923cf2b | 4711 (4639, 4729) | 445 | | **Adult** | | | | | | Escort/Sex workers advertising | 30eaed17-06d8-11ef-b6e1-027612b7f6b5 | 56e6729e-06fa-11ef-bbf8-02d3d923cf2b | 9609 (7312) | 81299 | | High Street Sex Shop | 30ea7552-06d8-11ef-b6e1-027612b7f6b5 | 56e66f7b-06fa-11ef-bbf8-02d3d923cf2b | 4778 (4761) | 459999 | | Massage Parlours | 30eb0852-06d8-11ef-b6e1-027612b7f6b5 | 56e673ed-06fa-11ef-bbf8-02d3d923cf2b | 9604 (9602) | 81219 | | Online Adult Services | 30ebb7bc-06d8-11ef-b6e1-027612b7f6b5 | 56e6790e-06fa-11ef-bbf8-02d3d923cf2b | 9609 (6312) | 81299 | | Online Sex Shop | 30eab905-06d8-11ef-b6e1-027612b7f6b5 | 56e67159-06fa-11ef-bbf8-02d3d923cf2b | 4791 (4649) | 459999 | | Phone Chat | 30eb1ccf-06d8-11ef-b6e1-027612b7f6b5 | 56e67536-06fa-11ef-bbf8-02d3d923cf2b | 9609 (8220) | 561422 | | Pornographic video sharing and pornography website | 30eb3d15-06d8-11ef-b6e1-027612b7f6b5 | 56e6767c-06fa-11ef-bbf8-02d3d923cf2b | 6312 (5911) | 51929 | | Strip Club | 30eb7b12-06d8-11ef-b6e1-027612b7f6b5 | 56e677c4-06fa-11ef-bbf8-02d3d923cf2b | 9329 (5630) | 7139 | | **Arms and Defense** | | | | | | Manufacture and supply of military fighting vehicles | 30ec17af-06d8-11ef-b6e1-027612b7f6b5 | 56e67ba5-06fa-11ef-bbf8-02d3d923cf2b | 3040 (2910) | 336992 | | Manufacture and supply of weapons and ammunition | 30ec4337-06d8-11ef-b6e1-027612b7f6b5 | 56e67cef-06fa-11ef-bbf8-02d3d923cf2b | 2540 (2529, 2051) | 332994 | | Military equipment | 30ec7fb7-06d8-11ef-b6e1-027612b7f6b5 | 56e67e4e-06fa-11ef-bbf8-02d3d923cf2b | 3040 (2599) | 3369 | | **Cash Intensive Business and High Value Dealers** | | | | | | Art Market/Art Collectables/Galleries and Antiques | 30ed78c0-06d8-11ef-b6e1-027612b7f6b5 | 56e6a0eb-06fa-11ef-bbf8-02d3d923cf2b | 4778 (9003) | 459920 | | Auctioneers | 30ee7396-06d8-11ef-b6e1-027612b7f6b5 | 56e6a8e3-06fa-11ef-bbf8-02d3d923cf2b | 8299 (4779) | 4251 | | Builders/Building materials | 30ee9302-06d8-11ef-b6e1-027612b7f6b5 | 56e6aa27-06fa-11ef-bbf8-02d3d923cf2b | 4752 (4673, 2361) | 236 | | Cash and Valuables-in-Transit | 30ecbbde-06d8-11ef-b6e1-027612b7f6b5 | 56e680d8-06fa-11ef-bbf8-02d3d923cf2b | 8010 (8020) | 56161 | | Construction | 30ed4a59-06d8-11ef-b6e1-027612b7f6b5 | 56e69f22-06fa-11ef-bbf8-02d3d923cf2b | 4120 (4399, 7111, 4312) | 23 | | Fast Moving consumer goods | 30ef0945-06d8-11ef-b6e1-027612b7f6b5 | 56e6aeb1-06fa-11ef-bbf8-02d3d923cf2b | 4639 (4649) | 45 | | Jewellery/Gold and Diamonds Dealers | 30ecf51a-06d8-11ef-b6e1-027612b7f6b5 | 56e69b9f-06fa-11ef-bbf8-02d3d923cf2b | 4777 (3212, 4648) | 42394 | | Motor Vehicles and Boat Traders | 30edc563-06d8-11ef-b6e1-027612b7f6b5 | 56e6a25a-06fa-11ef-bbf8-02d3d923cf2b | 4511 (4540) | 441 | | Nail Bars/Salon | 30ede2a4-06d8-11ef-b6e1-027612b7f6b5 | 56e6a3a6-06fa-11ef-bbf8-02d3d923cf2b | 9602 | 81211 | | Precious metals dealers that purchase metals from pawnbrokers and other secondary sources | 30ece137-06d8-11ef-b6e1-027612b7f6b5 | 56e68243-06fa-11ef-bbf8-02d3d923cf2b | 4672 (2441) | 42394 | | Scrap Metals Dealers/Warehouse | 30ee09b8-06d8-11ef-b6e1-027612b7f6b5 | 56e6a4ea-06fa-11ef-bbf8-02d3d923cf2b | 4677 (3831, 3811) | 42393 | | Storage facilities | 30ee2985-06d8-11ef-b6e1-027612b7f6b5 | 56e6a62e-06fa-11ef-bbf8-02d3d923cf2b | 5210 (4941, 5320) | 4931 | | Tour and Travel companies | 30eebc77-06d8-11ef-b6e1-027612b7f6b5 | 56e6ab63-06fa-11ef-bbf8-02d3d923cf2b | 7912 (7911, 7990) | 5615 | | Used motor vehicles dealers or auctions | 30eed232-06d8-11ef-b6e1-027612b7f6b5 | 56e6acaa-06fa-11ef-bbf8-02d3d923cf2b | 4511 (4519) | 4411 | | Wholesale Duty Goods (e.g. Alcohol and tobacco) | 30ee3a08-06d8-11ef-b6e1-027612b7f6b5 | 56e6a768-06fa-11ef-bbf8-02d3d923cf2b | 4634 (4635) | 424 | | **Charities/Non-profit organisation** | | | | | | Animal Charities | 30ef5367-06d8-11ef-b6e1-027612b7f6b5 | 56e6b416-06fa-11ef-bbf8-02d3d923cf2b | 9499 (7500, 9104) | 813312 | | Art and Culture Charities | 30ef79aa-06d8-11ef-b6e1-027612b7f6b5 | 56e6b54d-06fa-11ef-bbf8-02d3d923cf2b | 9499 (9001, 9002) | 813219 | | Education Charities | 30f02713-06d8-11ef-b6e1-027612b7f6b5 | 56e6b695-06fa-11ef-bbf8-02d3d923cf2b | 9499 (8559, 8560) | 813219 | | Environment Charities | 30f044ce-06d8-11ef-b6e1-027612b7f6b5 | 56e6b7fc-06fa-11ef-bbf8-02d3d923cf2b | 9499 (3900) | 813312 | | Health Charities | 30f0572f-06d8-11ef-b6e1-027612b7f6b5 | 56e6b960-06fa-11ef-bbf8-02d3d923cf2b | 9499 (8690) | 813212 | | Humanitarian support | 30ef4384-06d8-11ef-b6e1-027612b7f6b5 | 56e6b2c0-06fa-11ef-bbf8-02d3d923cf2b | 8899 (8810) | 6242 | | International Charities | 30f06a53-06d8-11ef-b6e1-027612b7f6b5 | 56e6baa3-06fa-11ef-bbf8-02d3d923cf2b | 9499 (8899) | 8133 | | Political Charities | 30f0a95b-06d8-11ef-b6e1-027612b7f6b5 | 56e6bbd8-06fa-11ef-bbf8-02d3d923cf2b | 9492 (9499) | 813319 | | Religious Charities | 30f0abe3-06d8-11ef-b6e1-027612b7f6b5 | 56e6bd0a-06fa-11ef-bbf8-02d3d923cf2b | 9491 (9499) | 8131 | | **Crowdfunding** | | | | | | Donation or rewards platforms | 30f0cc57-06d8-11ef-b6e1-027612b7f6b5 | 56e6bfca-06fa-11ef-bbf8-02d3d923cf2b | 6311 (8299) | 813219 | | Investment, equity or debt platforms | 30f1213b-06d8-11ef-b6e1-027612b7f6b5 | 56e6c242-06fa-11ef-bbf8-02d3d923cf2b | 6619 (6611) | 5239 | | Loan-based or peer-to-peer platforms | 30f13f60-06d8-11ef-b6e1-027612b7f6b5 | 56e6c37a-06fa-11ef-bbf8-02d3d923cf2b | 6619 (6492) | 5222 | | Non-Profit crowdfunding platform | 30f113b0-06d8-11ef-b6e1-027612b7f6b5 | 56e6c100-06fa-11ef-bbf8-02d3d923cf2b | 6619 (8899) | 813219 | | **Cryptocurrency business** | | | | | | Crypto OTC Trading | 30f19298-06d8-11ef-b6e1-027612b7f6b5 | 56e6c689-06fa-11ef-bbf8-02d3d923cf2b | 6619 (4791) | 5239 | | Cryptocurrency ATM | 30f1a174-06d8-11ef-b6e1-027612b7f6b5 | 56e6c7cf-06fa-11ef-bbf8-02d3d923cf2b | 6619 (4799) | 5223 | | Cryptocurrency Investment Advisory | 30f1df20-06d8-11ef-b6e1-027612b7f6b5 | 56e6ca53-06fa-11ef-bbf8-02d3d923cf2b | 6619 (6630) | 52394 | | Custodian wallet providers | 30f1a3e2-06d8-11ef-b6e1-027612b7f6b5 | 56e6c913-06fa-11ef-bbf8-02d3d923cf2b | 6619 (6311) | 52399 | | Unlicensed VC exchange | 30f20aa4-06d8-11ef-b6e1-027612b7f6b5 | 56e6ccdd-06fa-11ef-bbf8-02d3d923cf2b | 6619 | 5239 | | Virtual Currency Administrators or Miners | 30f1f3d0-06d8-11ef-b6e1-027612b7f6b5 | 56e6cb98-06fa-11ef-bbf8-02d3d923cf2b | 6311 (6499) | 5182 | | **Entertainment** | | | | | | Media production/online publishing/animation | 30f29530-06d8-11ef-b6e1-027612b7f6b5 | 56e6cf4b-06fa-11ef-bbf8-02d3d923cf2b | 5911 (6010) | 5162 | | Motion pictures/film, broadcast media | 30f2e949-06d8-11ef-b6e1-027612b7f6b5 | 56e6dc56-06fa-11ef-bbf8-02d3d923cf2b | 5911 (5914) | 5121 | | Movie production | 30f2ec48-06d8-11ef-b6e1-027612b7f6b5 | 56e6dda3-06fa-11ef-bbf8-02d3d923cf2b | 5911 (5912) | 5121 | | Music stores, musical instruments | 30f2d696-06d8-11ef-b6e1-027612b7f6b5 | 56e6db06-06fa-11ef-bbf8-02d3d923cf2b | 4759 (3220) | 459999 | | Radio, television, stereo | 30f2d49d-06d8-11ef-b6e1-027612b7f6b5 | 56e6d97c-06fa-11ef-bbf8-02d3d923cf2b | 4743 (2640) | 5131 | | Social Media platforms | 30f2d221-06d8-11ef-b6e1-027612b7f6b5 | 56e6d0f2-06fa-11ef-bbf8-02d3d923cf2b | 6312 (6311) | 516210 | | **Extractive Sector** | | | | | | Extraction of Natural Gas | 30f33a2c-06d8-11ef-b6e1-027612b7f6b5 | 56e6e17b-06fa-11ef-bbf8-02d3d923cf2b | 620 (3521) | 2111 | | Extraction of crude Petroleum | 30f315b9-06d8-11ef-b6e1-027612b7f6b5 | 56e6e035-06fa-11ef-bbf8-02d3d923cf2b | 610 (910) | 2111 | | Mining and Quarrying | 30f3693f-06d8-11ef-b6e1-027612b7f6b5 | 56e6e2bc-06fa-11ef-bbf8-02d3d923cf2b | 899 (729, 510) | 21 | | **Financial Services** | | | | | | Alternative banking platforms (ABPs) | 30f7d0b1-06d8-11ef-b6e1-027612b7f6b5 | 56e70c81-06fa-11ef-bbf8-02d3d923cf2b | 6419 (6619, 6201) | 522 | | Asset Finance | 30f4b66e-06d8-11ef-b6e1-027612b7f6b5 | 56e6f099-06fa-11ef-bbf8-02d3d923cf2b | 6491 (7711) | 5222 | | Authorised Bank | 30f46a4d-06d8-11ef-b6e1-027612b7f6b5 | 56e6eda5-06fa-11ef-bbf8-02d3d923cf2b | 6419 (6411) | 5221 | | Authorised Insurance Firm | 30f47f65-06d8-11ef-b6e1-027612b7f6b5 | 56e6ef22-06fa-11ef-bbf8-02d3d923cf2b | 6511 (6512, 6520) | 5241 | | Authorised Payment Institution (API) | 30f3a36b-06d8-11ef-b6e1-027612b7f6b5 | 56e6e528-06fa-11ef-bbf8-02d3d923cf2b | 6619 (6499) | 5223 | | Brokerage services to Funds | 30f4e70a-06d8-11ef-b6e1-027612b7f6b5 | 56e6f20e-06fa-11ef-bbf8-02d3d923cf2b | 6612 (6611) | 5231 | | Building Society | 30f3ec75-06d8-11ef-b6e1-027612b7f6b5 | 56e6e8d5-06fa-11ef-bbf8-02d3d923cf2b | 6419 (4110) | 5221 | | CFDs/Forex trading/Brokers | 30f86906-06d8-11ef-b6e1-027612b7f6b5 | 56e71ee0-06fa-11ef-bbf8-02d3d923cf2b | 6612 (6492) | 5231 | | Consumer Credit Providers | 30f52efd-06d8-11ef-b6e1-027612b7f6b5 | 56e6f6c3-06fa-11ef-bbf8-02d3d923cf2b | 6492 (8291) | 5222 | | Corporate Finance | 30f5457f-06d8-11ef-b6e1-027612b7f6b5 | 56e6f83d-06fa-11ef-bbf8-02d3d923cf2b | 6619 (6621) | 5231 | | Credit Card | 30f57fc5-06d8-11ef-b6e1-027612b7f6b5 | 56e6fa51-06fa-11ef-bbf8-02d3d923cf2b | 6619 (1812) | 5222 | | Credit Unions | 30f40acb-06d8-11ef-b6e1-027612b7f6b5 | 56e6ea55-06fa-11ef-bbf8-02d3d923cf2b | 6419 (9420) | 52213 | | Discretionary and advisory investment management | 30f59466-06d8-11ef-b6e1-027612b7f6b5 | 56e6fbd7-06fa-11ef-bbf8-02d3d923cf2b | 6630 (6619) | 52394 | | Electronic money institution (EMI) | 30f812d5-06d8-11ef-b6e1-027612b7f6b5 | 56e70dcb-06fa-11ef-bbf8-02d3d923cf2b | 6419 (6209) | 5223 | | Execution-only stockbrokers | 30f61068-06d8-11ef-b6e1-027612b7f6b5 | 56e6fed7-06fa-11ef-bbf8-02d3d923cf2b | 6612 (6619) | 5231 | | Financial Advisors | 30f630bb-06d8-11ef-b6e1-027612b7f6b5 | 56e7001b-06fa-11ef-bbf8-02d3d923cf2b | 6619 (6629) | 52394 | | Informal Value Transfer System (FINCEN definition) | 30f85922-06d8-11ef-b6e1-027612b7f6b5 | 56e71d97-06fa-11ef-bbf8-02d3d923cf2b | 6619 (6499) | 5223 | | Invoice Finance | 30f66497-06d8-11ef-b6e1-027612b7f6b5 | 56e70151-06fa-11ef-bbf8-02d3d923cf2b | 6492 (8291) | 5222 | | Life assurance, and life-related pensions and investment products | 30f66732-06d8-11ef-b6e1-027612b7f6b5 | 56e70289-06fa-11ef-bbf8-02d3d923cf2b | 6511 (6530) | 524113 | | Money Services Business (MSB) | 30f83498-06d8-11ef-b6e1-027612b7f6b5 | 56e70f2d-06fa-11ef-bbf8-02d3d923cf2b | 6619 (6499) | 5223 | | Motor Finance | 30f69e50-06d8-11ef-b6e1-027612b7f6b5 | 56e703cc-06fa-11ef-bbf8-02d3d923cf2b | 6491 (4511) | 5222 | | Name-passing brokers in inter-professional markets | 30f6dc3d-06d8-11ef-b6e1-027612b7f6b5 | 56e70508-06fa-11ef-bbf8-02d3d923cf2b | 6612 (6619) | 5231 | | Non-life providers of investment fund products | 30f75d62-06d8-11ef-b6e1-027612b7f6b5 | 56e7064b-06fa-11ef-bbf8-02d3d923cf2b | 6512 (6622) | 5259 | | PSD/EMD Agent | 30f5b372-06d8-11ef-b6e1-027612b7f6b5 | 56e6fd58-06fa-11ef-bbf8-02d3d923cf2b | 6619 (6499) | 5223 | | Penny stock or microcap securities (US SEC definition) | 30f50d6e-06d8-11ef-b6e1-027612b7f6b5 | 56e6f54a-06fa-11ef-bbf8-02d3d923cf2b | 6612 (6619) | 5239 | | Private Equity | 30f760b3-06d8-11ef-b6e1-027612b7f6b5 | 56e70791-06fa-11ef-bbf8-02d3d923cf2b | 6430 (6630) | 5239 | | Retail Banking | 30f837aa-06d8-11ef-b6e1-027612b7f6b5 | 56e716fc-06fa-11ef-bbf8-02d3d923cf2b | 6419 (6499) | 5221 | | Shell Banks | 30f4e9bb-06d8-11ef-b6e1-027612b7f6b5 | 56e6f3cd-06fa-11ef-bbf8-02d3d923cf2b | 6419 (6499) | 5221 | | Syndicated Loan | 30f79309-06d8-11ef-b6e1-027612b7f6b5 | 56e708cb-06fa-11ef-bbf8-02d3d923cf2b | 6492 (6619) | 5222 | | Trade Finance | 30f7b3ac-06d8-11ef-b6e1-027612b7f6b5 | 56e70a01-06fa-11ef-bbf8-02d3d923cf2b | 6492 (6619) | 5222 | | Unlicensed MSB | 30f854ec-06d8-11ef-b6e1-027612b7f6b5 | 56e71a80-06fa-11ef-bbf8-02d3d923cf2b | 6619 | 5223 | | Unregulated Foreign Exchange Provider | 30f4343b-06d8-11ef-b6e1-027612b7f6b5 | 56e6ec13-06fa-11ef-bbf8-02d3d923cf2b | 6619 | 5239 | | Virtual Asset Service Providers (VASPs) | 30f3e91f-06d8-11ef-b6e1-027612b7f6b5 | 56e6e79d-06fa-11ef-bbf8-02d3d923cf2b | 6619 (6499) | 5239 | | Wealth Management and Private Bank | 30f3a5b3-06d8-11ef-b6e1-027612b7f6b5 | 56e6e669-06fa-11ef-bbf8-02d3d923cf2b | 6630 (6619) | 52394 | | Wholesale Banking - Capital Markets | 30f85769-06d8-11ef-b6e1-027612b7f6b5 | 56e71c40-06fa-11ef-bbf8-02d3d923cf2b | 6419 (6612) | 5221 | | Wholesale Markets | 30f7cdb3-06d8-11ef-b6e1-027612b7f6b5 | 56e70b43-06fa-11ef-bbf8-02d3d923cf2b | 4690 (4611, 4619) | 42 | | **Gambling Business** | | | | | | Arcades | 30f8a1d9-06d8-11ef-b6e1-027612b7f6b5 | 56e722bd-06fa-11ef-bbf8-02d3d923cf2b | 9329 (7721) | 713120 | | Bettings | 30f8f01e-06d8-11ef-b6e1-027612b7f6b5 | 56e723f5-06fa-11ef-bbf8-02d3d923cf2b | 9200 (9319) | 7132 | | Bingo | 30f94758-06d8-11ef-b6e1-027612b7f6b5 | 56e72589-06fa-11ef-bbf8-02d3d923cf2b | 9200 | 7132 | | Casino | 30f9b94b-06d8-11ef-b6e1-027612b7f6b5 | 56e7283c-06fa-11ef-bbf8-02d3d923cf2b | 9200 (5510) | 7132 | | Casino - ship based, poker | 30f98d5e-06d8-11ef-b6e1-027612b7f6b5 | 56e726d9-06fa-11ef-bbf8-02d3d923cf2b | 9200 (5010) | 7132 | | Crypto Gambling (e.g., bitcoin Casino) | 30f9cc9e-06d8-11ef-b6e1-027612b7f6b5 | 56e7297e-06fa-11ef-bbf8-02d3d923cf2b | 9200 (6312) | 7132 | | Fantasy sports, iGaming, video games | 30fa23af-06d8-11ef-b6e1-027612b7f6b5 | 56e7302a-06fa-11ef-bbf8-02d3d923cf2b | 9200 (5821) | 7132 | | Gambling software | 30f9fc37-06d8-11ef-b6e1-027612b7f6b5 | 56e72ac8-06fa-11ef-bbf8-02d3d923cf2b | 5829 (6201) | 5132 | | Gaming machine | 30fa0e37-06d8-11ef-b6e1-027612b7f6b5 | 56e72c37-06fa-11ef-bbf8-02d3d923cf2b | 3240 (3399) | 3399 | | Lotteries | 30fa1e4c-06d8-11ef-b6e1-027612b7f6b5 | 56e72d97-06fa-11ef-bbf8-02d3d923cf2b | 9200 | 7132 | | Remote/Online | 30fa2141-06d8-11ef-b6e1-027612b7f6b5 | 56e72ee8-06fa-11ef-bbf8-02d3d923cf2b | 4791 (5310) | 51929 | | Sports Betting | 30f89f97-06d8-11ef-b6e1-027612b7f6b5 | 56e72159-06fa-11ef-bbf8-02d3d923cf2b | 9200 (9319) | 7132 | | Unlicensed Gambling business (remote and non-remote) | 30fa469c-06d8-11ef-b6e1-027612b7f6b5 | 56e731aa-06fa-11ef-bbf8-02d3d923cf2b | 9200 | 7132 | | **Independent legal professionals (firm and sole practitioner)** | | | | | | Accountancy Services | 30fad312-06d8-11ef-b6e1-027612b7f6b5 | 56e73b45-06fa-11ef-bbf8-02d3d923cf2b | 6920 (7022, 6311) | 541211 | | Auditor | 30fa6c0c-06d8-11ef-b6e1-027612b7f6b5 | 56e73483-06fa-11ef-bbf8-02d3d923cf2b | 6920 (7010) | 541211 | | Corporate Structures formation company | 30fae32a-06d8-11ef-b6e1-027612b7f6b5 | 56e73ca4-06fa-11ef-bbf8-02d3d923cf2b | 6910 (8211) | 5411 | | External Accountant | 30fb124c-06d8-11ef-b6e1-027612b7f6b5 | 56e73dec-06fa-11ef-bbf8-02d3d923cf2b | 6920 | 541219 | | Individuals who provide tax advice and/or aggressive tax schemes | 30fb4033-06d8-11ef-b6e1-027612b7f6b5 | 56e73f2d-06fa-11ef-bbf8-02d3d923cf2b | 6920 (7022) | 541213 | | Insolvency practitioner | 30fa6f39-06d8-11ef-b6e1-027612b7f6b5 | 56e7366b-06fa-11ef-bbf8-02d3d923cf2b | 6910 (6621) | 541199 | | Legal Services Firm | 30fb8a58-06d8-11ef-b6e1-027612b7f6b5 | 56e74073-06fa-11ef-bbf8-02d3d923cf2b | 6910 (6920) | 5411 | | Notaries | 30fa8ecd-06d8-11ef-b6e1-027612b7f6b5 | 56e737f9-06fa-11ef-bbf8-02d3d923cf2b | 6910 (8423) | 541199 | | Tax Adviser | 30fbac75-06d8-11ef-b6e1-027612b7f6b5 | 56e741fa-06fa-11ef-bbf8-02d3d923cf2b | 6920 (7022) | 541213 | | Trust and Company Services Providers (TCSPs) | 30fab353-06d8-11ef-b6e1-027612b7f6b5 | 56e73970-06fa-11ef-bbf8-02d3d923cf2b | 6910 (6430, 7490) | 5411 | | **Manufacturing** | | | | | | Automotive, accessories | 30fc76a0-06d8-11ef-b6e1-027612b7f6b5 | 56e7507a-06fa-11ef-bbf8-02d3d923cf2b | 4532 (4531, 4520) | 4413 | | Electronic manufacturing, consumer electronics | 30fcaca1-06d8-11ef-b6e1-027612b7f6b5 | 56e754a0-06fa-11ef-bbf8-02d3d923cf2b | 2640 (2611) | 334 | | Railroad manufacture | 30fc9849-06d8-11ef-b6e1-027612b7f6b5 | 56e75348-06fa-11ef-bbf8-02d3d923cf2b | 3020 (3317, 4212) | 3365 | | Shipbuilding, heavy machinery | 30fc78b1-06d8-11ef-b6e1-027612b7f6b5 | 56e751f0-06fa-11ef-bbf8-02d3d923cf2b | 3011 (3315, 3012) | 3366 | | Telecommunications, mechanical, industrial engineering, semiconductors | 30fc5ef1-06d8-11ef-b6e1-027612b7f6b5 | 56e74d2d-06fa-11ef-bbf8-02d3d923cf2b | 6110 (6120, 7112, 3342) | 517 | | **Online Services** | | | | | | Network security | 30fd90ec-06d8-11ef-b6e1-027612b7f6b5 | 56e75897-06fa-11ef-bbf8-02d3d923cf2b | 6203 (9511, 8020) | 5415 | | Online service providers | 30fd928c-06d8-11ef-b6e1-027612b7f6b5 | 56e759f9-06fa-11ef-bbf8-02d3d923cf2b | 6311 (6391) | 51929 | | Streaming, hosting, cloud services | 30fd8ec8-06d8-11ef-b6e1-027612b7f6b5 | 56e75742-06fa-11ef-bbf8-02d3d923cf2b | 6311 (5913) | 5182 | | **Other Companies** | | | | | | Aviation/Aerospace/airline | 30fdce2c-06d8-11ef-b6e1-027612b7f6b5 | 56e774af-06fa-11ef-bbf8-02d3d923cf2b | 5110 (3030, 3316, 5121) | 481 | | Business Consultancy | 30fdbd8f-06d8-11ef-b6e1-027612b7f6b5 | 56e764bf-06fa-11ef-bbf8-02d3d923cf2b | 7022 (7021) | 541611 | | Chemicals/Biotechnology | 30fdcb6a-06d8-11ef-b6e1-027612b7f6b5 | 56e77226-06fa-11ef-bbf8-02d3d923cf2b | 2059 (2014, 7211) | 3254 | | Commodity Trading Companies | 30fdb6bb-06d8-11ef-b6e1-027612b7f6b5 | 56e75cb6-06fa-11ef-bbf8-02d3d923cf2b | 4612 (4672) | 5231 | | Computers, hardware, software, IT | 30fdc9e7-06d8-11ef-b6e1-027612b7f6b5 | 56e770a3-06fa-11ef-bbf8-02d3d923cf2b | 6201 (6202, 9511) | 5415 | | Dating/Matchmaking websites | 30fdbbfb-06d8-11ef-b6e1-027612b7f6b5 | 56e7620d-06fa-11ef-bbf8-02d3d923cf2b | 9609 (6312) | 51929 | | Direct Selling (MLM) | 30fdbefb-06d8-11ef-b6e1-027612b7f6b5 | 56e766b6-06fa-11ef-bbf8-02d3d923cf2b | 4799 (4619) | 4599 | | Fashion, cloth, shoes, apparel, clothing stores, leather goods | 30fdba4d-06d8-11ef-b6e1-027612b7f6b5 | 56e7604f-06fa-11ef-bbf8-02d3d923cf2b | 4771 (1419, 4642) | 4591 | | Football Sector | 30fdc20b-06d8-11ef-b6e1-027612b7f6b5 | 56e769b7-06fa-11ef-bbf8-02d3d923cf2b | 9312 (9311) | 7112 | | Freelance Platforms/GIG Economy | 30fdc050-06d8-11ef-b6e1-027612b7f6b5 | 56e76874-06fa-11ef-bbf8-02d3d923cf2b | 7810 (7820, 7830) | 51929 | | General trading companies operating in free trade zones | 30fdc364-06d8-11ef-b6e1-027612b7f6b5 | 56e76b3a-06fa-11ef-bbf8-02d3d923cf2b | 4690 (5210) | 42 | | Graphic Design/Web design | 30fdc6ca-06d8-11ef-b6e1-027612b7f6b5 | 56e76e1b-06fa-11ef-bbf8-02d3d923cf2b | 7410 (1813) | 54143 | | Holding Companies | 30fdb8ac-06d8-11ef-b6e1-027612b7f6b5 | 56e75e57-06fa-11ef-bbf8-02d3d923cf2b | 6420 (7010) | 5511 | | Human resources/staffing/recruiting/payroll services | 30fdcf8c-06d8-11ef-b6e1-027612b7f6b5 | 56e775ec-06fa-11ef-bbf8-02d3d923cf2b | 7810 (7830) | 5613 | | Marine services, fishery | 30fdc525-06d8-11ef-b6e1-027612b7f6b5 | 56e76c91-06fa-11ef-bbf8-02d3d923cf2b | 311 (1020, 321) | 1141 | | Packing, containers, warehousing, facilities services/logistics | 30fdccdc-06d8-11ef-b6e1-027612b7f6b5 | 56e7736e-06fa-11ef-bbf8-02d3d923cf2b | 5229 (5210, 8292, 5224) | 493 | | Printing/Photography/Publishing | 30fdc85c-06d8-11ef-b6e1-027612b7f6b5 | 56e76f64-06fa-11ef-bbf8-02d3d923cf2b | 1812 (1723, 1811) | 323 | | **Pharmaceutical and Medicine** | | | | | | Alternative medicines | 30fdd491-06d8-11ef-b6e1-027612b7f6b5 | 56e779a3-06fa-11ef-bbf8-02d3d923cf2b | 4774 (8690) | 621399 | | Cannabidiol (CBD) Distributor | 30fdd5fc-06d8-11ef-b6e1-027612b7f6b5 | 56e77ae8-06fa-11ef-bbf8-02d3d923cf2b | 4646 (4645) | 424 | | Cannabidiol (CBD) Manufacturer | 30fdd76c-06d8-11ef-b6e1-027612b7f6b5 | 56e77c27-06fa-11ef-bbf8-02d3d923cf2b | 2120 (128, 2110) | 325 | | Cannabidiol (CBD) Retailer | 30fdd8d6-06d8-11ef-b6e1-027612b7f6b5 | 56e77d65-06fa-11ef-bbf8-02d3d923cf2b | 4773 (4775) | 459999 | | Medical, dental equipment and supplies | 30fdda4d-06d8-11ef-b6e1-027612b7f6b5 | 56e77e9e-06fa-11ef-bbf8-02d3d923cf2b | 4646 (3250) | 3391 | | Nutraceuticals | 30fdd2d5-06d8-11ef-b6e1-027612b7f6b5 | 56e77872-06fa-11ef-bbf8-02d3d923cf2b | 1089 (4729) | 311999 | | **Public Sector** | | | | | | Civic/social organization/defence and space | 30fde5a5-06d8-11ef-b6e1-027612b7f6b5 | 56e78875-06fa-11ef-bbf8-02d3d923cf2b | 9499 (8810) | 8134 | | Consulate | 30fddd22-06d8-11ef-b6e1-027612b7f6b5 | 56e78101-06fa-11ef-bbf8-02d3d923cf2b | 9900 (8421) | 92 | | Diplomatic mission | 30fdde9a-06d8-11ef-b6e1-027612b7f6b5 | 56e78239-06fa-11ef-bbf8-02d3d923cf2b | 9900 (8422) | 92 | | Embassies | 30fde018-06d8-11ef-b6e1-027612b7f6b5 | 56e7836e-06fa-11ef-bbf8-02d3d923cf2b | 9900 (8411) | 92 | | Govt administration/relations/Judiciary/Legislative office | 30fde2d2-06d8-11ef-b6e1-027612b7f6b5 | 56e785dc-06fa-11ef-bbf8-02d3d923cf2b | 8411 (8412, 8413) | 92 | | International Affairs, Trade and development | 30fde431-06d8-11ef-b6e1-027612b7f6b5 | 56e78721-06fa-11ef-bbf8-02d3d923cf2b | 9900 (8421) | 92 | | Museums | 30fde184-06d8-11ef-b6e1-027612b7f6b5 | 56e784a8-06fa-11ef-bbf8-02d3d923cf2b | 9102 (9103) | 7121 | | **Real Estate Activities** | | | | | | Other letting and operating of own or leased real estate | 30fbc49a-06d8-11ef-b6e1-027612b7f6b5 | 56e74470-06fa-11ef-bbf8-02d3d923cf2b | 6820 (6810) | 5311 | | Real estate agencies | 30fbc985-06d8-11ef-b6e1-027612b7f6b5 | 56e745c6-06fa-11ef-bbf8-02d3d923cf2b | 6831 (6832) | 5312 | | Renting and operating of Housing Association real estate | 30fbdb1d-06d8-11ef-b6e1-027612b7f6b5 | 56e74708-06fa-11ef-bbf8-02d3d923cf2b | 6820 (8790) | 5311 | | **Wildlife** | | | | | | Illegal Wildlife Trade | 30fc0ad0-06d8-11ef-b6e1-027612b7f6b5 | 56e7497a-06fa-11ef-bbf8-02d3d923cf2b | 4623 (9900) | 424 | | Wildlife Trade | 30fc3549-06d8-11ef-b6e1-027612b7f6b5 | 56e74ac7-06fa-11ef-bbf8-02d3d923cf2b | 4623 (149, 170) | 424 | --- ## Monthly expected volumes This is a definitive list of Expected Monthly Volume values which can be included in the payload when creating a customer. ## Retrieve monthly expected volumes To get the list with monthly expected volumes, send the [`GET accounts/monthly-expected-volumes`](../../api-explorer/endpoints/get-monthly-expected-volumes) request. In the response, you receive the existing ranges. | Attribute | Description | | ----------- | ------------------------------------------------------------------------------------------- | | `reference` | Unique identifier for the volume range. For example, "ef779c41-547e-11ef-8b9c-027612b7f6b5" | | `name` | Name/description of the volume range. For example, "0 - 500 000.00 EUR" | | `min` | Minimum expected volume within the range. For example, 0 | | `max` | Maximum expected volume for within range. For example, 500.000. | ```json SANDBOX [ { "reference": "ef779c41-547e-11ef-8b9c-027612b7f6b5", "name": "0 - 500 000.00 EUR", "min": 0, "max": 500000 }, { "reference": "ef77a16d-547e-11ef-8b9c-027612b7f6b5", "name": "500 000.00 - 2 000 000.00 EUR", "min": 500000, "max": 2000000 }, { "reference": "ef77a2bc-547e-11ef-8b9c-027612b7f6b5", "name": "2 000 000.00 - 5 000 000.00 EUR", "min": 2000000, "max": 5000000 }, { "reference": "ef77a3db-547e-11ef-8b9c-027612b7f6b5", "name": "More than 5 000 000.00 EUR", "min": 5000000, "max": 5000000 }, { "reference": "ef77a4f8-547e-11ef-8b9c-027612b7f6b5", "name": "I'm not sure", "min": 0, "max": 500000 } ] ``` | Name | Minimum | Maximum | Reference | | ------------------------------- | ------- | ------- | ------------------------------------ | | 0 - 500 000.00 EUR | 0 | 500000 | ef779c41-547e-11ef-8b9c-027612b7f6b5 | | 500 000.00 - 2 000 000.00 EUR | 500000 | 2000000 | ef77a16d-547e-11ef-8b9c-027612b7f6b5 | | 2 000 000.00 - 5 000 000.00 EUR | 2000000 | 5000000 | ef77a2bc-547e-11ef-8b9c-027612b7f6b5 | | More than 5 000 000.00 EUR | 5000000 | 5000000 | ef77a3db-547e-11ef-8b9c-027612b7f6b5 | | I'm not sure | 0 | 500000 | ef77a4f8-547e-11ef-8b9c-027612b7f6b5 | ```json PRODUCTION [ { "reference": "d8805674-53fa-11ef-9628-02d3d923cf2b", "name": "0 - 500 000.00 EUR", "min": 0, "max": 500000 }, { "reference": "d8805cd2-53fa-11ef-9628-02d3d923cf2b", "name": "500 000.00 - 2 000 000.00 EUR", "min": 500000, "max": 2000000 }, { "reference": "d8805ea7-53fa-11ef-9628-02d3d923cf2b", "name": "2 000 000.00 - 5 000 000.00 EUR", "min": 2000000, "max": 5000000 }, { "reference": "d8805fdd-53fa-11ef-9628-02d3d923cf2b", "name": "More than 5 000 000.00 EUR", "min": 5000000, "max": 5000000 }, { "reference": "d8806120-53fa-11ef-9628-02d3d923cf2b", "name": "I'm not sure", "min": 0, "max": 500000 } ] ``` | Name | Minimum | Maximum | Reference | | ------------------------------- | ------- | ------- | ------------------------------------ | | 0 - 500 000.00 EUR | 0 | 500000 | d8805674-53fa-11ef-9628-02d3d923cf2b | | 500 000.00 - 2 000 000.00 EUR | 500000 | 2000000 | d8805cd2-53fa-11ef-9628-02d3d923cf2b | | 2 000 000.00 - 5 000 000.00 EUR | 2000000 | 5000000 | d8805ea7-53fa-11ef-9628-02d3d923cf2b | | More than 5 000 000.00 EUR | 5000000 | 5000000 | d8805fdd-53fa-11ef-9628-02d3d923cf2b | | I'm not sure | 0 | 500000 | d8806120-53fa-11ef-9628-02d3d923cf2b | --- ## Payment schemes BVNK provides multiple payment schemes to facilitate seamless digital asset transactions. This reference page covers all available payment options and their features. :::info Cut-off time (COT) Settlements arrive within the stated timeframes only if payments are submitted by the stipulated cut-off time (COT). ::: ## Payment schemes | Scheme | Description | Settlement | COT (working days) | Currency | | :--- | :--- | :--- | :--- | :---: | | `ACH` | Electronic network for processing low-value, batch transactions including direct deposits and bill payments. | Next business day (T+1) if submitted by COT | GMT 01:45–08:00pm | USD | | `ACH_SAME_DAY` | Faster ACH processing, typically settling same business day. If requested outside the available window, processed as next-day (fee may still apply). | Same day (T+0) if submitted by COT | GMT 01:45–08:00pm | USD | | `FEDNOW` | Federal Reserve instant payment system providing 24/7 real-time payment capabilities. | Instant | 24/7 | USD | | `FEDWIRE` | Real-time gross settlement system operated by the U.S. Federal Reserve for high-value, time-critical transactions. | Within one business day if submitted by COT | GMT 01:45–08:00pm | USD | | `RTP` | Real-Time Payments network enabling instant fund transfers between participating U.S. banks, 24/7. | Instant | 24/7 | USD | | `SWIFT` | Global messaging network for secure international financial transactions between banks worldwide. | 1–5 business days if submitted by COT | GMT 07:00am–02:00pm | USD, EUR, GBP | | Scheme | Description | Settlement | COT (working days) | Currency | | :--- | :--- | :--- | :--- | :---: | | `SEPA_CT` | SEPA Credit Transfer for euro-denominated bank transfers across Europe with uniform standards. | 1–2 business days if submitted by COT | GMT 07:00am–02:00pm | EUR | | `SEPA_INST` | SEPA Instant Payment for real-time euro transfers across Europe, 24/7. May incur a small fee. | Seconds if submitted by COT | GMT 07:00am–02:00pm | EUR | | `SWIFT` | Global messaging network for secure international financial transactions between banks worldwide. | 1–5 business days if submitted by COT | GMT 07:00am–02:00pm | USD, EUR, GBP | | Scheme | Description | Settlement | COT (working days) | Currency | | :--- | :--- | :--- | :--- | :---: | | `FASTER_PAYMENT` | UK service enabling near-instantaneous bank transfers, typically settling within seconds to hours. | Minutes to hours if submitted by COT | GMT 07:00am–02:00pm | GBP | | `SWIFT` | Global messaging network for secure international financial transactions between banks worldwide. | 1–5 business days if submitted by COT | GMT 07:00am–02:00pm | USD, EUR, GBP | | Scheme | Description | Settlement | COT | Currency | | :--- | :--- | :--- | :--- | :---: | | `BOOK` | Internal transfer of funds between accounts within the same financial institution. | Instant | N/A | USD, EUR, GBP | :::tip COT in other timezones - **US** (ACH, ACH_SAME_DAY, FEDWIRE): | Timezone | Winter (Standard) | Summer (Daylight) | | :--- | :--- | :--- | | PT | 05:45am-12:00pm PST | 06:45am-01:00pm PDT | | ET | 08:45am-03:00pm EST | 09:45am-04:00pm EDT | - **Europe and UK** (SEPA, SWIFT, FASTER_PAYMENT): | Timezone | Winter (Standard) | Summer (Daylight) | | :--- | :--- | :--- | | PT | 11:00pm-06:00am PST | 12:00am-07:00am PDT | | ET | 02:00am-09:00am EST | 03:00am-10:00am EDT | ::: ## Transaction limits In most cases, the supported payment schemes have daily limits that are practically unlimited. For example, for local USD transactions (ACH, FEDWIRE, RTP, FEDNOW), our bank partners set an overall cap of approximately $50 million with no specific limits at the customer level. But note that `SEPA_INST` has a maximum transaction limit of €1,000,000 per transaction. Contact your account manager to learn the specific limits for a particular payment scheme or to request an increase. There is no **minimum** transaction amount, however zero-value transactions are not permitted. --- ## Third-party providers ## PSD2: What it is and why it's important The revised Payment Services Directive (PSD2) was enacted on 13 January 2018 across Europe and the UK. PSD2 introduced new rights for certain third-party providers (TPPs) to directly access payment service users' online payment accounts in certain circumstances and requires Account Servicing Payment Service Providers (ASPSPs), to permit access through a dedicated interface built on APIs. ## What will you be able to do? To meet our PSD2 requirements, BVNK will allow Third-Party Providers (TPPs) certain access: * Account Information Services (AIS): Account Information from BVNK accounts will be able to be shared with other financial institutions, upon the account owner's consent.\ *E.g.: a budgeting application that consolidates account information from multiple banks and financial institutions will be able to import data from BVNK virtual accounts.* [Read more on AIS on the Open Banking website.](https://standards.openbanking.org.uk/customer-experience-guidelines/introduction/customer-journey-consent/v3-1-5/) * Payment Initiation Services (PIS): payments to other financial institutions will be able to be initiated directly from other bank accounts, with the owner's consent.\ *E.g.: a payment of a credit card balance can be made from a mobile application and biometric authentication, with the money coming out of a BVNK virtual account.* [Read more on PIS on the Open Banking website.](https://standards.openbanking.org.uk/customer-experience-guidelines/payment-initiation-services/latest/) ## Register your interest If you are interested, please fill in [the form](https://docs.google.com/forms/d/e/1FAIpQLSfMS90jgvzC_zosrA-rMxVfWT4dYRavIsJv9blVr41yeZuD9A/viewform?embedded=true) below: --- ## Uptime and incident handling ## Our commitment to uptime At BVNK, we understand the critical importance of maintaining consistent and reliable service. We are dedicated to providing an **uptime of 99.90% availability**. This commitment is not just a goal; it's an integral part of our service promise. 99.9 % uptime/availability results in the following periods of allowed downtime/unavailability: * Daily: 1m 26s * Weekly: 10m 4.8s * Monthly: 43m 28s * Quarterly: 2h 10m 24s * Yearly: 8h 41m 38s ## What is an incident? In the context of our services, an incident is defined as any unexpected event that disrupts our business operational processes or reduces the quality of the service we provide. Recognising the potential impact of such events, we have put robust measures in place to minimise their occurrence and severity. ### Monitoring and immediate response Our systems are under continuous surveillance with sophisticated monitoring tools. Our goal is to ensure that any system outages are detected as quickly as possible. In the event of an incident, our on-call engineers are promptly notified via an escalation management tool. This rapid response is a cornerstone of our incident management protocol. ### 24/7 engineer availability We ensure that there is always an engineer on duty, 24 hours a day, 7 days a week, 365 days a year. This round-the-clock availability guarantees that expert help is always at hand to address and resolve any issues as swiftly as possible. ### Stay informed To keep our customers informed about, planned interruptions due to maintenance, uptime and any incidents, we communicate actively through our status page [https://bvnk.status.io/](https://bvnk.status.io/). We encourage you to subscribe to this service to stay updated with the latest information regarding the status of our systems. ### Learning and improvement Following any incident, we engage in a thorough learning process to understand and address the root cause. This includes conducting a Post-Mortem for every incident, which is completed within 48 hours of resolution. The key outcome of this process is to identify and implement actions that drive continuous improvement in our systems and processes. ### Continuous improvement We believe in the power of learning from incidents. The most important outcome of our Post-Mortem process is not just understanding what happened, but ensuring that the actions identified are implemented. This commitment to continuous improvement helps us in enhancing our systems and reducing the likelihood of future incidents. --- ## Webhook signature validator When implementing BVNK's webhook system, signature validation is critical for security. The BVNK Webhook Signature Validator is a client-side tool that helps developers verify webhook signatures from BVNK's payment processing system. This tool allows you to: - Validate webhook signatures without sending sensitive data to any external servers. - Troubleshoot signature validation issues in your implementation. - Compare your generated signatures against signatures received from BVNK. - Identify configuration problems in your webhook handling code. :::warning Security Note All validation happens in your browser—no sensitive information is transmitted to any server, making this tool safe to use with production credentials. ::: --- ## How to use the validator ### Gather required information Before using the validator, collect the following information: | Field | Description | |-------|-------------| | **Secret Key** | Your unique webhook secret key provided by BVNK | | **Webhook URL** | The complete URL where you receive webhooks (**Standard** only) | | **Received Signature** | The signature value from the `X-Signature` header of the webhook | | **Content Type** | The content type of the webhook, typically `application/json` (**Standard** only) | | **JSON Payload** | The complete body content of the webhook | ### Enter information into the validator 1. Select the appropriate tab for your webhook type. 2. Enter your **Secret Key** in the designated field. 3. For **Standard** webhooks: Input the complete **Webhook URL**, including the protocol (`https://`). 4. Paste the **Received Signature** exactly as it appears in the `X-Signature` header. 5. For **Standard** webhooks: Verify the **Content Type** matches. 6. Paste the **JSON Payload** exactly as received, maintaining the same formatting (raw format). 7. Click the **"Validate Signature"** button. The tool will process the information locally in your browser and display the results showing whether the signature is valid or invalid. ### Review diagnostic information If the signature validation fails, review the diagnostic information section, which provides: | Diagnostic Field | Description | |------------------|-------------| | **Webhook Path** | Shows the extracted path from the URL (**Standard** only) | | **String Used for Generating Signature** | Displays the exact string that was used to create the signature | | **Generated Signature** | The signature calculated by the tool | | **Received Signature** | The signature you entered for comparison | --- ## Choose your webhook type BVNK uses two different signature formats depending on the webhook service. Select the appropriate validator for your integration: This validator is for webhooks that use **hexadecimal HMAC-SHA256** signatures. The signature is generated from the combination of webhook path, content type, and payload. ```json { "source": "payment", "event": "statusChanged", ... } ``` --- ## Send payments To create a fiat payout for **local payment** rails, you must have the following: * Recipient's account number. * Recipient's bank account details: * For GBP payments: SCAN and IBAN * For EUR payments: IBAN * For USD payments: ABA routing number If invalid bank details are provided, such as an incorrect bank code or account number, the payout request will be rejected with an error message returned. To create a fiat payout for **international payments** via SWIFT, you must first acquire the following information: * Payee's full name or company name and address. * Bank Account Number (IBAN or other, depending on the currency) * Name and address of the payee's bank * Payee's SWIFT or Bank identifier Code (BIC) bank code. The following diagram shows the flow of a fiat pay out. ## Create payout To create a payout, send the [`POST /payment/v2/payouts`](../../../api-explorer/endpoints/payout-create-v-2) request with the payload: ```json Payout Request Example for Individual { "walletId": "a:24092324785677:o6rhCEZ:1", "amount": 1000.5, "currency": "EUR", "method": "SEPA_CT", "reference": "PAYOUT-2025-001", "beneficiary": { "remittanceInformation": "Payment for services", "entity": { "type": "INDIVIDUAL", "firstName": "John", "lastName": "Dove", "dateOfBirth": "15-01-1990", "relationshipType": "SELF_OWNED", "address": { "addressLine1": "123 Main St", "city": "New York", "region": "NY", "postCode": "10001", "country": "US" } }, "bankAccount": { "accountNumber": "000123456789", "accountType": "CHECKING", "bank": { "identificationCode": "ATFEUS33", "nid": "021000021", "address": { "addressLine1": "383 MADISON AVENUE", "city": "New York", "region": "NY", "postCode": "10001", "country": "US" } } } }, "fees": { "customerFee": { "amount": 5, "currency": "USD" } }, "metadata": { "source": "api", "merchantId": "merchantOne" }, "requestDetails": { "originator": { "ipAddress": "10.0.0.2" } } } ``` ```json Payout Request Example for Company { "walletId": "a:24092324785677:o6rhCEZ:1", "amount": 5000, "currency": "EUR", "method": "SEPA_INST", "reference": "CORPORATE-PAYOUT-001", "beneficiary": { "remittanceInformation": "Invoice payment #INV-2024-001", "entity": { "type": "COMPANY", "legalName": "Acme Corporation Ltd", "relationshipType": "SELF_OWNED", "address": { "addressLine1": "456 Business Park", "addressLine2": "Suite 100", "city": "Dublin", "region": "Leinster", "postCode": "D02 XY45", "country": "IE" } }, "bankAccount": { "accountNumber": "IE29 AIBK 9311 5212 3456 78", "accountType": "BUSINESS_CHECKING", "bank": { "identificationCode": "AIBKIE2D", "address": { "addressLine1": "Allied Irish Bank", "city": "Dublin", "country": "IE" } } } }, "fees": { "customerFee": { "amount": 15, "currency": "EUR" } } } ``` While EUR local transfers have fewer requirements, for USD payments and SWIFT transactions, it's advisable to provide comprehensive details to prevent potential rejection by the banking partner. In the successful response, you receive the standard payment details. ```json Payout Response Example for Individual { "id": "550e8400-e29b-41d4-a716-446655440000", "method": "SEPA_CT", "reference": "PAYOUT-2025-001", "status": "PROCESSING", "type": "payment:payout:fiat", "direction": "OUT", "fees": { "customerFee": { "amount": 5, "currency": "EUR" }, "processingFee": { "amount": 1, "currency": "EUR" } }, "originator": { "amount": 1000.5, "currency": "EUR", "walletId": "a:24092324785677:o6rhCEZ:1" }, "beneficiary": { "remittanceInformation": "Payment for services", "amount": 1000.5, "currency": "EUR", "entity": { "type": "INDIVIDUAL", "firstName": "John", "lastName": "Dove", "relationshipType": "BENEFICIARY" }, "bankAccount": { "accountNumber": "000123456789", "bank": { "identificationCode": "ATFEUS33" } } }, "createdAt": "2024-01-15T10:30:00Z", "updatedAt": "2024-01-15T10:35:00Z", "metadata": { "source": "api", "merchantId": "merchantOne" } } ``` ```json Payout Response Example for Company { "id": "660f9511-f30c-52e5-b827-557766551111", "method": "SEPA_INST", "reference": "CORPORATE-PAYOUT-001", "status": "COMPLETED", "type": "payment:payout:fiat", "direction": "OUT", "fees": { "customerFee": { "amount": 15, "currency": "EUR" }, "processingFee": { "amount": 1, "currency": "EUR" } }, "originator": { "amount": 5000, "currency": "EUR", "walletId": "a:24092324785677:o6rhCEZ:1" }, "beneficiary": { "remittanceInformation": "Invoice payment #INV-2024-001", "amount": 4985, "currency": "EUR", "entity": { "type": "COMPANY", "legalName": "Acme Corporation Ltd", "relationshipType": "BENEFICIARY" }, "bankAccount": { "accountNumber": "IE29 AIBK 9311 5212 3456 78", "bank": { "identificationCode": "AIBKIE2D" } } }, "createdAt": "2024-01-15T09:00:00Z", "updatedAt": "2024-01-15T11:30:00Z" } ``` In case of payment in Euro, the transaction is subject to **Verification of Payee (VoP)**. ## Request VoP **VoP** is a SEPA (Single Euro Payments Area) service that verifies a beneficiary's name against their IBAN to prevent fraud and errors. Before a SEPA Credit Transfer (SCT) or SEPA Instant Transfer (SCT inst) is authorized, the originator's bank will check the provided name and IBAN with the beneficiary's bank and report the result (Match, Close match, or No match) back to the originator. This is a regulatory requirement under the Instant Payments Regulation to enhance security for euro transfers within the SEPA zone. ![VoP flow](/img/bvnk/use-cases/fiat-payout-2.png) When performing VoP, the system may return one of the following results: * **Successful verification**: The beneficiary's name matches. The payment proceeds automatically. * **Partial match**: The provided name is similar to the one associated with the IBAN, but not an exact match. You may still send the payment **at your own risk**, but there is a chance it may not be processed successfully. * **No match**: The beneficiary's name does not match at all. You may still attempt to send the payment, but the likelihood of rejection increases. * **Failed verification** due to other reasons: * For instance, **technical** issues may arise if the verification service is unavailable. * Others, for example, if a beneficiary's bank decides not to conduct verification. All results, except for the successful verification, indicate that if the beneficiary's name in the request doesn't match the name in the beneficiary's bank account, the bank will issue a warning about the inconsistency. In this case, the status 202 is returned. ```json { "id": "UUID", "requiredAction": "CONFIRM_BENEFICIARY", "status": "PAYEE_VERIFICATION_REQUIRED", "verificationResult": { "matchStatus": "PARTIAL_MATCH", "actualName": "George D Collins", // Only present if status is Partial Match "providedName": "George Colline" } } ``` ```json { "id": "UUID", "requiredAction": "CONFIRM_BENEFICIARY", "status": "PAYEE_VERIFICATION_REQUIRED", "verificationResult": { "matchStatus": "NO_MATCH", "providedName": "Gerorge Colline" } } ``` ```json { "id": "UUID", "requiredAction": "CONFIRM_BENEFICIARY", "status": "PAYEE_VERIFICATION_REQUIRED", "verificationResult": { "matchStatus": "TECHNICAL_ERROR", "providedName": "Gerorge Colline" } } ``` ```json { "id": "UUID", "requiredAction": "CONFIRM_BENEFICIARY", "status": "PAYEE_VERIFICATION_REQUIRED", "verificationResult": { "matchStatus": "NOT_POSSIBLE", "providedName": "Gerorge Colline" } } ``` :::warning 🚧 Your transaction is not created yet. Status 202 means that you must send another request to confirm the operation. See Send confirmation below. ::: You can also test how verification works in the sandbox. For that, use the following test names and IBANs, when sending the payment request: * First Name: John Smith * Last Name: Doe * IBAN: MT45SYPL39480744067217548372015 * BIC: SYPLMTM2XXX * First Name: John * Last Name: Doe * IBAN: MT45SYPL39480744067217548372015 * BIC: SYPLMTM2XXX * First Name: Jane * Last Name: Mary * IBAN: MT45SYPL39480744067217548372015 * BIC: SYPLMTM2XXX There are two ways of verifying the beneficiary's name: * As part of the Payout process: by calling another endpoint to confirm the payout operation. See [Confirm payout](#confirm-payout). * As a separate two-step process, where you verify a beneficiary via a separate endpoint and then make the payout using the received `verificationId`. See [Integrate two-step verification](#integrate-two-step-verification). ### Confirm payout You can use this method after making a payout in euros described earlier and receive a response with any of the following `status` values : * `PARTIAL_MATCH` * `NO_MATCH` * `NOT_POSSIBLE` * `TECHNICAL_ERROR` The statuses indicate that the beneficiary's name doesn't match the one associated with the beneficiary's bank account or cannot be verified. If you are aware of this inconsistency and would like to proceed with the payment anyway, send a [`POST /payment/v2/payouts/{id}/actions`](../../../api-explorer/endpoints/payout-confirm-beneficiary-v-2) request with `id` of the transaction received in the 202 response and the following body. ```json Example Payload { "type": "CONFIRM_BENEFICIARY" } ``` In the successful response, you receive the standard payout details, as shown earlier. If the operation is not confirmed within five minutes, it expires. If you provide the wrong transaction ID or send the confirmation request after its expiration, an error is returned. In this case, you will have to create a new payout. ### Integrate two-step verification Alternatively, you can verify the beneficiary's name using the verification endpoint. Note that in case you follow this approach, step 1 of the following procedure must be completed before creating the payout. To verify the beneficiary's name, do the following: 1. Send the [`POST /platform/v1/beneficiaries/verification`](../../../api-explorer/endpoints/verify-beneficiary) request. ```json Example Request { "accountNumber": "GB29MDTR60000131926819", "bankCode": "MDTRGB2L", "firstName": "John", "lastName": "Smith", "businessName": "Acme Corporation Ltd", "currency": "GBP" } ``` In the successful response, you receive the name match status and validation period: ```json Example Response { "id" : "550e8400-e29b-41d4-a716-446655440000", "matchStatus" : "MATCH", "providedName" : "John Smith", "actualName": "J. Smith", // only present for partial match "validUntil" : "2025-01-15T14:30:00Z" // when the verification will expire } ``` 2. Send the [`POST /payment/v2/payouts`](../../../api-explorer/endpoints/payout-create-v-2) request as described earlier. Remember to include the verification `Id`, acquired on step 1 in the response, as the `X-Verification-Id` header. If you provide a valid `verificationId` regardless of status, BVNK verifies that it exists in the records and is not expired, then executes the payment regardless of the `matchStatus` value. The following outcomes are possible: * If the verification session is still valid, the payout will be created, and there will be no need to verify the beneficiary. * If the verification session is expired or `verificationId` is not found within BVNK records, the payout will not be created, and you will receive a `400` error. ## Check payout status To check the status of a payout, send the [`GET /payment/v2/payouts/{transactionReference}`](../../../api-explorer/endpoints/payout-get-v-2) request. In the successful response, you receive the details on the transaction and its status. ```json Response { "transactionReference": "123e4567-e89b-12d3-a456-426614174000", "paymentReference": "Payment for invoice #12345", "amount": { "value": 1000, "currency": "GBP" }, "fee": { "value": 0.00, "currency": "GBP" }, "walletId": "a:24071743003474:xE95Oq7:1", "status": "PROCESSING", "createdAt": "2024-09-26T14:30:00Z", "details": { "type": "FIAT", "beneficiary": { "entityType": "COMPANY", "businessDetails": { "businessName": "Doe Corp" }, "address": { "addressLine1": "123 Main St", "addressLine2": "Apt 4B", "region": "London", "city": "London", "country": "DE", "postCode": "EC2R 8AH", "fullAddress": "123 Main St Suite 500" }, "bankAccount": { "format": "IBAN", "bankName": "Meridian Trust Bank", "accountNumber": "GB29MDTR60000131926819", "bankCode": "MDTRGB2L", "bankAddress": { "addressLine1": "123 Main St", "addressLine2": "Suite 500", "region": "London", "city": "London", "country": "DE", "postCode": "12345", "fullAddress": "123 Main St Suite 500" } }, "correspondentBic": "ABCDEF12" } }, "metadata": { "field1": "field1 value", "field2": "field2 value" } } ``` Also, you can listen to the following webhooks to get notified when the status of the payment changes: * [Fiat payout status change](../../../api-explorer/bvnk-webhooks/payment-payout-status-change-v-2) * [Fiat pay-in status change](../../../api-explorer/bvnk-webhooks/payment-payin-status-change-v-2) --- ## Prepare for integration(Virtual-accounts) ## Sandbox account and API credentials The sandbox environment mirrors production but operates with test data, allowing you to build and validate your integration without financial risk. Your sandbox account provides access to the BVNK Portal where you manage wallets, configure integrations, and monitor transactions. API credentials use Hawk authentication, a cryptographic scheme that signs each request without exposing your secret key. When configuring API keys, consider which operations your integration requires. Payouts and withdrawals require IP address allowlisting for security. Read-only operations like fetching transaction data have fewer restrictions. Steps to complete this phase: - Contact your Account Manager to request a sandbox account. - After you have it, you can [Create API keys](../../../get-started/generate-api-keys) there. --- ## Fiat wallet setup Virtual accounts are linked to fiat wallets, which store your funds. Once BVNK enables virtual accounts based on your business requirements, your wallet is assigned a unique identifier, such as a vIBAN or ABA routing number. Accounts can be issued in your business name or your customers' names for personalised payment routing. To create a wallet, send the `POST /ledger/v2/wallets` request with a selected profile. For details on body parameters, refer to the [Create wallet](../../../api-explorer/endpoints/ledger-wallet-create-v-2) endpoint. ```json { "profileId": "fiat:eur:abcd1234", "name": "My EUR Wallet", } ``` After creating the wallet, locate its ID in the **Wallet** section of the Portal. You will need this ID when creating payments. ![WalletId](/img/bvnk/use-cases/walletId.png) For more information and available wallet actions, see [Create a wallet](../../../get-started/set-up-wallets). --- ## Webhook integration Webhooks provide real-time notifications when transaction states change, eliminating the need for continuous polling. Your webhook endpoint receives HTTP POST requests from BVNK whenever relevant events occur—payment confirmations, status changes, customer onboarding updates, and more. Design your endpoint to acknowledge receipt immediately (return HTTP 200) and process the event asynchronously to avoid timeouts. Webhook payloads include cryptographic signatures for authenticity verification. Implement signature validation to ensure requests originate from BVNK and haven't been tampered with. Steps to complete this phase: - [Configure webhooks](../../../get-started/create-webhook-listener) for endpoint setup and signature verification examples. --- ## Integration testing Use the sandbox environment to validate your implementation before going live. Test both success paths and error scenarios to ensure your system handles edge cases gracefully. For pay-ins, create payment channels and simulate incoming transactions to verify your webhook handling and balance updates. For payouts, send test transactions to destination wallets and confirm delivery. If using the Embedded model, test the full customer onboarding lifecycle. Optional external wallets like [MetaMask](../../../get-started/set-up-metamask-ethereum-wallet-for-eth-and-usdt-erc20-tokens) (Ethereum) or [TronLink](../../../get-started/set-up-tronlink-wallet-for-trx-and-usdt-trc20-tokens) (Tron) can simulate end-user interactions during testing. --- ## Go live Production activation requires coordination with your Account Manager. Sandbox configuration does not transfer automatically—you must replicate settings in your live account. Update your codebase to use production credentials and endpoints. The base URL changes from `https://api.sandbox.bvnk.com/` to `https://api.bvnk.com/`. Generate new API keys in your live Portal account and configure webhooks with production endpoint URLs. Before processing real transactions, verify your live wallets have sufficient balance for payouts and confirm all webhook subscriptions are active. --- ## Next steps Once a virtual account is created, you can immediately: - [Send payments](../send-payment-via-va) from the virtual account to your customers. - [Receive payments](../receive-fiat-pay-ins) from your customers into the virtual account. - [Refund payments](../refund-payments-via-va) from the virtual account to your customers. - [Check your transactions](../../../reconciliation/reconcile-transactions) with reconciliation reports. - [Track your transactions](../../../get-started/create-webhook-listener) with webhook notifications. --- ## Provide virtual account to your customers export const namedExample = `{ "id": "a:25052061784520:mVeJ2rA:1", "name": "USD SWIFT", "customer": { "id": "a1f84b6c-9e23-4d17-b590-7c4a83de2f10", "name": "Meridian Healthcare" }, "status": "ACTIVE", "balance": { "amount": 52080.00, "currency": "USD" }, "paymentInstruments": [ { "type": "FIAT", "accountHolderName": "MERIDIAN HEALTHCARE SERVICES LTD", "accountNumber": "GB29MDTR60000131926819", "bankDetails": { "name": "Meridian Trust Bank PLC", "bic": "MDTRGB2LXXX", "nid": { "value": "600001", "type": "SORT_CODE" } } } ], "createdAt": "2025-05-20T10:17:45.200Z", "updatedAt": "2025-06-15T09:22:31.480Z" }`; export const pooledExample = `{ "id": "a:25031472839104:kR7mHxQ:1", "name": "USD SWIFT pooled", "customer": { "id": "a1f84b6c-9e23-4d17-b590-7c4a83de2f10", "name": "Meridian Healthcare" }, "status": "ACTIVE", "balance": { "amount": 48250.75, "currency": "USD" }, "paymentInstruments": [ { "type": "FIAT", "accountHolderName": "System Pay Services (Malta) Limited", "accountNumber": "MT84MALT011000012345MTLCAST001S", "remittanceInformationPrefix": "REF DZ0XJL4", "bankDetails": { "name": "Bank of Valletta plc", "bic": "VALLMTMT" } } ], "createdAt": "2025-03-14T09:12:33.104Z", "updatedAt": "2025-06-02T11:45:07.463Z" }`; As a licensed financial institution, you can create virtual accounts for your own customers so that each customer receives a vIBAN issued in their name. Every action you perform for customer virtual accounts is the same as for your own [direct virtual accounts](./prepare-for-va-integration.mdx)—you prepare for integration, send payments, receive funds via SWIFT, and refund pay-ins in exactly the same way. This guide covers only the steps and payloads that are specific to the customer use case. For any action not described here, follow the corresponding direct virtual account guide. --- ## Prepare for integration Preparing for customer virtual accounts follows the same steps as [Prepare for integration](./prepare-for-va-integration.mdx): set up your sandbox account and API credentials, create a fiat wallet, then test and go live. The only additional step is creating the customer before you create their wallet. Once the customer exists, you create their fiat wallet by sending the [`POST /ledger/v2/wallets`](../../../api-explorer/endpoints/ledger-wallet-create-v-2) request, the same as in the direct guide, but with the `customerId` set to the customer's `reference`: ```json { "customerId": "a7e21c62-27b8-4b3b-b51e-eb10edeb1731", "currency": "USD", "name": "USD SWIFT", "profileId": "fiat:usd:ijkm9012" } ``` ### Create the customer Before creating a customer, collect information about them. To create an account for your customer, send the [`POST /platform/v2/customers`](/bvnk/api-explorer/endpoints/create-customer-v-2/) request with the payload specific to the customer type. Apart from the typical information, include the [industry reference](/bvnk/references/industy-references/) number and the [expected monthly volumes](/bvnk/references/monthly-expected-volumes) in the request payload. Unlike an Embedded Customer, **you don't need to provide agreements or upload documents** to verify the customer. For the detailed description of the body parameters, see the [Create customer](/bvnk/api-explorer/endpoints/create-customer-v-2/) endpoint. ```json { "type": "COMPANY", "company": { "name": "TechSolutions GmbH", "description": "Software development and IT consulting services", "taxResidenceCountryCode": "DE", "registrationNumber": "HRB 123456", "industryReference": "30fdc9e7-06d8-11ef-b6e1-027612b7f6b5", "monthlyExpectedVolumesReference": "ef77a16d-547e-11ef-8b9c-027612b7f6b5", "address": { "addressLine1": "Unter den Linden 77", "city": "Berlin", "postalCode": "10117", "countryCode": "DE" }, "representative": { "firstName": "Hans", "lastName": "Mueller", "dateOfBirth": "1980-11-29", "birthCountryCode": "DE", "address": { "address1": "Unter den Linden 77", "address2": "Apartment 9", "postalCode": "10117", "city": "Berlin", "countryCode": "DE" } } } } ``` ```json { "type": "INDIVIDUAL", "individual": { "firstName": "Sophie", "lastName": "Weber", "dateOfBirth": "1985-08-15", "birthCountryCode": "DE", "documentNumber": "C01XY 123456", "address": { "addressLine1": "Friedrichstraße 123", "city": "Berlin", "postalCode": "10117", "countryCode": "DE" }, } } ``` In the successful response, you receive a unique customer `reference` for tracking the verification status, which starts as `PENDING` and becomes `VERIFIED` or `REJECTED` once verification completes. ```json Success { "reference": "a7e21c62-27b8-4b3b-b51e-eb10edeb1731", "status": "PENDING" } ``` Use the returned `reference` as the `customerId` when you create the customer's wallet, as shown above. --- ## Fiat wallet setup Virtual accounts are linked to fiat wallets, which store your funds. Once BVNK enables virtual accounts based on your business requirements, your wallet is assigned a unique identifier, such as a vIBAN or ABA routing number. Accounts can be issued in your business name or your customers' names for personalised payment routing. To create a wallet for your customer, send the `POST /ledger/v2/wallets` request with a selected profile. For details on body parameters, refer to the [Create wallet](../../../api-explorer/endpoints/ledger-wallet-create-v-2) endpoint. ```json { "profileId": "fiat:eur:abcd1234", "name": "EUR Wallet", "customer": { "id": "a7e21c62-27b8-4b3b-b51e-eb10edeb1731" } } ``` After creating the wallet, locate its ID in the **Wallet** section of the Portal. You will need this ID when creating payments. ![WalletId](/img/bvnk/use-cases/walletId.png) For more information and available wallet actions, see [Create a wallet](../../../get-started/set-up-wallets). --- ## Create internal transfers Customer virtual accounts additionally let you move funds between the accounts you manage. You can transfer funds between a partner and a customer, or between customers of the same partner. Create a transfer by sending the [`POST /payment/v3/transfers`](/bvnk/api-explorer/endpoints/transfer-create-v-3/) request. The `originator.walletId` is the originating wallet, and `beneficiary.walletId` is the destination wallet: ```json Request { "reference": "REF558628", "originator": { "amount": 111, "currency": "EUR", "walletId": "a:25032550863140:zKwR3P9:1" }, "beneficiary": { "currency": "EUR", "walletId": "a:25021926815866:4jlPfFg:1" }, "metadata": { "memberId": "987654321" } } ``` To check the status of the transfer, listen to the [Transfer webhook](../../../api-explorer/bvnk-webhooks/payment-transfer-status-change-v-3) or send the [`GET /payment/v3/transfers/{transferId}`](/bvnk/api-explorer/endpoints/transfer-read-v-3/) request. In the successful response, you receive the transfer details, including the originating and beneficiary entities: ```json Response { "id": "ae29acc3-c54a-11f0-90b6-21c3f364ff25", "reference": "REF558628", "status": "COMPLETED", "type": "payment:transfer", "method": "BOOK", "fees": { "processingFee": { "amount": 0, "currency": "EUR" } }, "originator": { "amount": 111, "currency": "EUR", "entity": { "legalName": "3Com", "type": "COMPANY" }, "walletId": "a:25032550863140:zKwR3P9:1" }, "beneficiary": { "amount": 111, "currency": "EUR", "entity": { "legalName": "4COM", "type": "COMPANY" }, "walletId": "a:25021926815866:4jlPfFg:1" }, "metadata": { "memberId": "987654321" }, "createdAt": "2025-11-19T13:21:38.870144Z", "updatedAt": "2025-11-19T13:21:38.870144Z" } ``` --- ## Send payments Sending payments from a customer's virtual account follows the same flow as [Send payments](./send-payment-via-va.mdx), including Verification of Payee (VoP) for EUR transfers. The only customer-specific difference is that when you make a payout from a customer's wallet, you must include the customer's IP address in the `requestDetails.originator.ipAddress` field: ```json { "walletId": "a:24092324785677:o6rhCEZ:1", "amount": 1000.5, "currency": "EUR", "method": "SEPA_CT", "beneficiary": { "remittanceInformation": "Payment for services", "entity": { "type": "INDIVIDUAL", "firstName": "John", "lastName": "Dove", "relationshipType": "SELF_OWNED", "address": { "addressLine1": "123 Main St", "city": "New York", "region": "NY", "postCode": "10001", "country": "US" } }, "bankAccount": { "accountNumber": "000123456789", "bank": { "identificationCode": "ATFEUS33" } } }, "requestDetails": { "originator": { "ipAddress": "10.0.0.2" } } } ``` --- ## Refund payments Refunding pay-ins made to a customer's virtual account is identical to [Refund payments](./refund-payments-via-va.mdx)—there is no customer-specific payload. Follow the direct guide to estimate, initiate, and track refunds. --- **What's next?** - [Send payments](./send-payment-via-va.mdx) from a customer's virtual account. - [Refund payments](./refund-payments-via-va.mdx) from a customer's virtual account. - [Check your transactions](../../../reconciliation/reconcile-transactions) with reconciliation reports. - [Track your transactions](../../../get-started/create-webhook-listener) with webhook notifications. --- ## Receive fiat pay-ins BVNK virtual accounts accept fiat pay-ins in EUR, GBP, and USD across a range of payment rails, including SWIFT, SEPA, ACH, Faster Payments, and others. For the full list of supported methods, see [Payment methods](../../../references/payment-methods). If your counterparties already have your virtual account banking details, no further setup is required. When a pay-in is received, BVNK credits the funds and sends a [Fiat pay-in status change](../../../api-explorer/bvnk-webhooks/payment-payin-status-change-v-2) webhook with `COMPLETED` status. Subscribe to this webhook to process incoming payments automatically. The following sections explain how to retrieve and share your SWIFT deposit details with senders who do not have them. --- ## Prerequisites Before depositing funds, ensure you have the following: - A fiat wallet with the SWIFT payment method enabled. See [Set up wallets](../../../get-started/set-up-wallets). - A registered bank account on your BVNK Settings page (for Portal deposits). ## Request deposit details Each wallet is issued with unique SWIFT account details in your business name. Senders transfer funds directly to the account number; no payment reference is required. Retrieve deposit details via the API or BVNK Portal to share with the sender. 1. Send the [`GET ledger/v2/wallets/{id}`](../../../api-explorer/endpoints/ledger-wallet-read-v-2) request. ```json { "id": "a:25060945127830:rTgK3wN:1", "name": "USD SWIFT", "status": "ACTIVE", "balance": { "amount": 0.00, "currency": "USD" }, "paymentInstruments": [ { "type": "FIAT", "accountHolderName": "Greenfield Logistics Ltd", "accountNumber": "846203917458", "bankDetails": { "name": "Atlantic Federal Bank", "bic": "ATFEUS33", "nid": { "value": "621353", "type": "SORT_CODE" } } } ], "createdAt": "2025-06-09T14:05:12.830Z", "updatedAt": "2025-06-09T14:05:17.384Z" } ``` 2. In the response, locate the `paymentInstruments` array and extract these fields: - `accountHolderName` - `accountNumber` - `bankDetails.bic` 1. Go to the Portal and navigate to the **Wallets** section. 2. In the **Wallets** list, select the fiat wallet you want to fund. 3. Open the **Wallet Details** tab and review the displayed account details: - Beneficiary name - Account number or IBAN - Bank code **Provide payment instructions to senders** Share these details with anyone sending funds to your wallet: | Parameter | API field name | Description | | :---- | :---------- | :---------- | | Beneficiary name | `accountHolderName` | The name of the account holder for the BVNK SWIFT account. | | Account number | `accountNumber` | The IBAN or account number for the BVNK SWIFT account. | | Bank code | `bankDetails.bic` | The bank identifier code for the receiving bank. | ## Receive funds via SWIFT If your wallet uses a pooled SWIFT account, multiple wallets share a single bank account issued under the BVNK-owned legal entity. BVNK uses the `remittanceInformationPrefix` to identify which wallet each incoming payment belongs to. This reference is unique to your wallet and is required for transaction reconciliation. When you receive a SWIFT pay-in to a pooled account, the process is as follows: 1. The sender initiates a payment from their bank to the BVNK pooled SWIFT account, including the `remittanceInformationPrefix` as the payment reference. 2. BVNK receives the payment and matches it to your wallet using the reference. 3. BVNK credits the funds to your wallet. If the payment reference is missing or incorrect, BVNK cannot automatically match the payment. In this case, provide proof of payment, such as a bank statement or transfer confirmation, so BVNK can manually credit your wallet. This may delay the availability of your funds. Retrieve the `remittanceInformationPrefix` via the API or BVNK Portal and share it with your senders. 1. Send the [`GET ledger/v2/wallets/{id}`](../../../api-explorer/endpoints/ledger-wallet-read-v-2) request. ```json { "id": "a:25031472839104:kR7mHxQ:1", "name": "USD SWIFT pooled", "status": "ACTIVE", "balance": { "amount": 48250.75, "currency": "USD" }, "paymentInstruments": [ { "type": "FIAT", "accountHolderName": "System Pay Services (Malta) Limited", "accountNumber": "MT84MALT011000012345MTLCAST001S", "remittanceInformationPrefix": "REF DZ0XJL4", "bankDetails": { "name": "Bank of Valletta plc", "bic": "VALLMTMT" } } ], "createdAt": "2025-03-14T09:12:33.104Z", "updatedAt": "2025-06-02T11:45:07.463Z" } ``` 2. In the response, locate the `paymentInstruments` array and extract these fields: - `accountNumber` - `bankDetails.bic` - `remittanceInformationPrefix` 1. Go to the Portal and navigate to the **Wallets** section. 2. In the **Wallets** list, select the fiat wallet you want to fund. 3. Click **Deposit**. ![](/img/bvnk/use-cases/deposit-fiat.png) 4. Review the displayed account details: - Account holder name - Account number or IBAN - SWIFT/BIC code - Payment reference ![](/img/bvnk/use-cases/deposit-details.png) **Provide payment instructions to senders** Share these details with anyone sending funds to your wallet: | Parameter | API field name | Description | | :---- | :---------- | :---------- | | Beneficiary name | `accountHolderName` | The name of the account holder for the BVNK pooled SWIFT account. | | Account number | `accountNumber` | The IBAN or account number for the BVNK pooled SWIFT account. | | SWIFT/BIC code | `bankDetails.bic` | The bank identifier code for the receiving bank. | | Payment reference | `remittanceInformationPrefix` | Unique reference for your wallet. Senders must include this value in the payment instructions so BVNK can allocate the funds to your wallet. | :::info Ensure senders use the same `remittanceInformationPrefix` value for all future transfers to your BVNK wallet. ::: --- ## Process incoming payments When BVNK processes a SWIFT pay-in, the [Fiat pay-in status change](../../../api-explorer/bvnk-webhooks/payment-payin-status-change-v-2) webhook delivers payment details with `COMPLETED` status. Use this notification to reconcile incoming funds and trigger downstream workflows. For pooled SWIFT accounts, the `originator.remittanceInformation` field in the webhook payload contains the payment reference your sender included. Match it against your wallet's `remittanceInformationPrefix` to confirm the payment was routed correctly. ```json { "event": "payment:v2:payin:status-change", "eventId": "019a37b9-a750-7d71-ac8e-06a6d7368ed8", "timestamp": "2025-10-31T00:45:05.232849572Z", "data": { "id": "019a37b9-609b-7e34-a16e-3f6e16bcc0aa", "fees": { "processingFee": { "amount": 10.04, "currency": "EUR" } }, "type": "payment:v2:payin:status-change", "method": "SWIFT", "status": "COMPLETED", "createdAt": "2025-10-31T00:44:47.131901Z", "direction": "IN", "updatedAt": "2025-10-31T00:45:03.328045Z", "originator": { "amount": 22, "entity": { "type": "INDIVIDUAL", "lastName": "Gomez", "firstName": "Maria", "relationshipType": "SELF_OWNED" }, "currency": "EUR", "bankAccount": { "bank": { "nid": null, "address": { "city": "MADRID", "region": "", "country": "ES", "postCode": "28013", "addressLine1": "LEGANITOS 47", "addressLine2": "" }, "identificationCode": "EAPFESM2", "name": "CaixaBank, S.A." }, "accountNumber": "ES8968490001590001393179" }, "remittanceInformation": "REF HA3ZQ91" }, "beneficiary": { "amount": 22, "entity": { "type": "COMPANY", "legalName": "3Com" }, "currency": "EUR", "walletId": "a:24100345791536:AkqHCea:1" }, "metadata": { "internalUserId": "user-451" }, "trackingDetails": [ { "externalTransactionId": "123e4567-e89b-12d3-a456-426614174000", "uetr": "550e8400-e29b-41d4-a716-446655440000" } ] } } ``` --- ## Test in sandbox To simulate a SWIFT pay-in in the sandbox, send the [`POST /payment/v2/payins/simulation`](../../../api-explorer/endpoints/simulate-payin-v-2) request: ```json { "walletId": "a:24092328494070:G5i4XZ9:1", "method": "SWIFT", "remittanceInformation": "REF DZ0XJL4 Invoice 12345", "amount": 1500, "currency": "USD", "originator": { "name": "Emily Kaldwin", "bankAccount": { "accountNumber": "GB87SYPE04082500000904", "accountNumberFormat": "IBAN", "bankCode": "SRLGGB2L" } } } ``` For additional simulation options, see [Try fiat payments in simulator](../../../get-started/payment-sim). --- **What's next?** - [Send payments](../send-payment-via-va) - [Refund fiat pay-ins](../refund-payments-via-va) - [Configure webhooks](../../../get-started/create-webhook-listener) to receive real-time notifications about incoming payments --- ## Refund payments(Virtual-accounts) --- ## Refund payments(3) The Refund API allows you to instantly refund any previous pay-in with a single click. It automatically creates the transaction without entering payout information. ## Prerequisites To process a refund, the original pay-in transaction must have been made to a BVNK account. You can find the necessary transaction references in the webhook events from the initial pay-in or by sending the [`GET /ledger/v1/transactions`](../../../api-explorer/endpoints/wallet-list-transactions) request. ## Limitations and rules * Refunds are returned via the original payment method. If that's not possible, the system will try to re-route funds via alternative means. * If you need to return funds to a different beneficiary, you can make it as a [new Payout](initiate-payment-2.mdx). * Refunds cannot exceed the original pay-in total amount. * Refunds can be performed in two ways: * **Fully**: The entire amount is refunded in one transaction.\ Note that only full refunds are supported for **Automated Clearing House** (ACH) transfers. * **Partially**: The portion of the amount is refunded in multiple transactions until reaching the original total.\ For example, instead of making one payment of £150, you can make separate transactions of £50, £70, and £30. In this case, three requests must be sent to the same `/refund` endpoint. :::info Standard payout fees Standard payout fees can be applied to each transaction per your agreement with BVNK. ::: ## Estimate refund You can check the estimated fee before initiating a refund to understand the costs. To estimate the refund via API, do the following: 1. From the [Pay-in webhook](../../../api-explorer/bvnk-webhooks/payment-payin-status-change-v-2) that you configured earlier, get `{transactionReference}`, the unique transaction ID you received previously and want to refund. Alternatively, you can make a GET request to the [transactions](../../../api-explorer/endpoints/wallet-list-transactions) endpoint to get the transaction list and select the reference ID of the required one. 2. Send the [`POST payment/v1/payins/{transactionReference}/refund/estimate`](../../../api-explorer/endpoints/estimate-refund-fee) request. In the path, specify `{transactionReference}` acquired in the previous step and include the following parameters in the request: When you receive the response, note the following: * `fee` is a charge applied when processing refunds. When a refund is issued, both the refund amount and the associated fee are deducted from your wallet. * `maxAvailableToRefund` indicates the remaining amount of the refund. ```json Example response { "fee": { "value": 1.00, "currency": "EUR" }, "maxAvailableToRefund": { "value": 150.00, "currency": "EUR" } } ``` ## Refund pay-in To refund any pay-in to your customers via API, do the following: 1. Get `{transactionReference}`, as described [earlier](#estimate-refund). 2. Send a [`POST payment/v1/payins/{transactionReference}/refund`](../../../api-explorer/endpoints/refund-payin) request. to the endpoint. In the path, specify `{transactionReference}`, acquired in the previous step, and include the following parameters. Once the refund is initiated, a new transaction item is generated in the portal's wallet. This new payout will adhere to the standard payout state machine process. ```json Example request (full refund) { "paymentReference": "order123456", "amount": { "value": "150", "currency": "EUR" } } ``` ```json Example request (partial refund) { "paymentReference": "order123456", "amount": { "value": "50", "currency": "EUR" } } ``` When you receive the response, note the following: * `fee` is a charge applied when processing refunds. When a refund is issued, both the refund amount and the associated fee are deducted from your wallet. * `transactionReference` indicates the unique ID of the refund transaction. ```json Example response { "transactionReference": "018e5e83-ac50-4df0-a7e4-c9f15d493f90", "fee": { "value": 1.00, "currency": "EUR" } } ``` 3. If you have a [4-eyes Approval flow](https://help.bvnk.com/hc/en-us/articles/18632401764626-4-Eye-Approval-flows) configured, you may need to confirm the operation on the BVNK Portal. See Cancel refund for more information. If you don't have any approval flows configured, the refund will be approved automatically. 4. To check the status of the refund, send a `GET /payment/v1/payins/{transactionReference}/refund` request to the endpoint. For more information on parameters, see [Check the Status of a Payout](../../bvnk/../../api-explorer/endpoints/payout-get-v-2). ## Approve or cancel refund You can cancel or approve a refund if you have previously configured 4-eyes Approval and have access to the BVNK Portal. To cancel a refund: 1. On the BVNK Portal, navigate to **Approvals** in the main menu and select the **Pending** tab. 2. Find the specific refund you wish to cancel. 3. Click **Reject** next to that refund. If you want to approve the refund instead, or if the API endpoint requires confirmation, click **Approve** or the confirmation button. :::info Once rejected, the refund operation cannot be reversed. Ensure you have selected the correct transaction before proceeding. ::: ## Handle failed refunds When a pay-in refund is attempted but cannot be completed, the API will respond with a `400 Bad Request` error. This error response will include a detailed message explaining the reason for the failure. If the refund for a pay-in isn't possible, consider making a [new Payout](initiate-payment-2.mdx). --- ## Send payments(Virtual-accounts) To create a fiat payout for **local payment** rails, you must have the following: * Recipient's account number. * Recipient's bank account details: * For GBP payments: SCAN and IBAN * For EUR payments: IBAN * For USD payments: ABA routing number If invalid bank details are provided, such as an incorrect bank code or account number, the payout request will be rejected with an error message returned. To create a fiat payout for **international payments** via SWIFT, you must first acquire the following information: * Payee's full name or company name and address. * Bank Account Number (IBAN or other, depending on the currency) * Name and address of the payee's bank * Payee's SWIFT or Bank identifier Code (BIC) bank code. The following diagram shows the flow of a fiat pay out. | Status | Description | | :----- | :---------- | | `PENDING_APPROVAL` | Optional. This state is returned if payouts from the wallet require approvals. The payout will remain in this state until all approvals are received. After that, the **PROCESSING** webhook is emitted. Only relevant for **fiat transactions**. | | `PROCESSING` | Payouts will remain in this state until a final response has been received from our processing partners. | | `COMPLETED` | Once a payout has been successfully completed from the side of BVNK, the payout will transition to COMPLETED. Note that it is still possible for the funds to be rejected by the beneficiary bank. | | `FAILED` | If for any reason our processing partners are unable to successfully complete the payout, the payout will transition to FAILED, and funds will remain in the merchant's wallet. | | `CANCELLED` | Should the payment be cancelled, then the status will be transitioned to the CANCELLED state. This can occur in scenarios such as when a four-eyes approval is rejected or the request of a four-eyes approval times out. Funds will be returned to the merchants wallet. | | `RETURNED` | Optional. Should the funds be returned after we have received a response from our processing partners, the payout will be marked as returned and the funds will be returned to the merchants wallet. | | `ON_HOLD` | This status is shown when screening takes longer than anticipated. | ## Create payout To create a payout, send the [`POST /payment/v2/payouts`](../../../api-explorer/endpoints/payout-create-v-2) request with the payload: ```json Payout Request Example for Individual { "walletId": "a:24092324785677:o6rhCEZ:1", "amount": 1000.5, "currency": "EUR", "method": "SEPA_CT", "reference": "PAYOUT-2025-001", "beneficiary": { "remittanceInformation": "Payment for services", "entity": { "type": "INDIVIDUAL", "firstName": "John", "lastName": "Dove", "dateOfBirth": "15-01-1990", "relationshipType": "SELF_OWNED", "address": { "addressLine1": "123 Main St", "city": "New York", "region": "NY", "postCode": "10001", "country": "US" } }, "bankAccount": { "accountNumber": "000123456789", "accountType": "CHECKING", "bank": { "identificationCode": "ATFEUS33", "nid": "021000021", "address": { "addressLine1": "383 MADISON AVENUE", "city": "New York", "region": "NY", "postCode": "10001", "country": "US" } } } }, "fees": { "customerFee": { "amount": 5, "currency": "USD" } }, "metadata": { "source": "api", "merchantId": "merchantOne" }, "requestDetails": { "originator": { "ipAddress": "10.0.0.2" } } } ``` ```json Payout Request Example for Company { "walletId": "a:24092324785677:o6rhCEZ:1", "amount": 5000, "currency": "EUR", "method": "SEPA_INST", "reference": "CORPORATE-PAYOUT-001", "beneficiary": { "remittanceInformation": "Invoice payment #INV-2024-001", "entity": { "type": "COMPANY", "legalName": "Acme Corporation Ltd", "relationshipType": "SELF_OWNED", "address": { "addressLine1": "456 Business Park", "addressLine2": "Suite 100", "city": "Dublin", "region": "Leinster", "postCode": "D02 XY45", "country": "IE" } }, "bankAccount": { "accountNumber": "IE29 AIBK 9311 5212 3456 78", "accountType": "BUSINESS_CHECKING", "bank": { "identificationCode": "AIBKIE2D", "address": { "addressLine1": "Allied Irish Bank", "city": "Dublin", "country": "IE" } } } }, "fees": { "customerFee": { "amount": 15, "currency": "EUR" } } } ``` While EUR local transfers have fewer requirements, for USD payments and SWIFT transactions, it's advisable to provide comprehensive details to prevent potential rejection by the banking partner. In the successful response, you receive the standard payment details. ```json Payout Response Example for Individual { "id": "550e8400-e29b-41d4-a716-446655440000", "method": "SEPA_CT", "reference": "PAYOUT-2025-001", "status": "PROCESSING", "type": "payment:payout:fiat", "direction": "OUT", "fees": { "customerFee": { "amount": 5, "currency": "EUR" }, "processingFee": { "amount": 1, "currency": "EUR" } }, "originator": { "amount": 1000.5, "currency": "EUR", "walletId": "a:24092324785677:o6rhCEZ:1" }, "beneficiary": { "remittanceInformation": "Payment for services", "amount": 1000.5, "currency": "EUR", "entity": { "type": "INDIVIDUAL", "firstName": "John", "lastName": "Dove", "relationshipType": "BENEFICIARY" }, "bankAccount": { "accountNumber": "000123456789", "bank": { "identificationCode": "ATFEUS33" } } }, "createdAt": "2024-01-15T10:30:00Z", "updatedAt": "2024-01-15T10:35:00Z", "metadata": { "source": "api", "merchantId": "merchantOne" } } ``` ```json Payout Response Example for Company { "id": "660f9511-f30c-52e5-b827-557766551111", "method": "SEPA_INST", "reference": "CORPORATE-PAYOUT-001", "status": "COMPLETED", "type": "payment:payout:fiat", "direction": "OUT", "fees": { "customerFee": { "amount": 15, "currency": "EUR" }, "processingFee": { "amount": 1, "currency": "EUR" } }, "originator": { "amount": 5000, "currency": "EUR", "walletId": "a:24092324785677:o6rhCEZ:1" }, "beneficiary": { "remittanceInformation": "Invoice payment #INV-2024-001", "amount": 4985, "currency": "EUR", "entity": { "type": "COMPANY", "legalName": "Acme Corporation Ltd", "relationshipType": "BENEFICIARY" }, "bankAccount": { "accountNumber": "IE29 AIBK 9311 5212 3456 78", "bank": { "identificationCode": "AIBKIE2D" } } }, "createdAt": "2024-01-15T09:00:00Z", "updatedAt": "2024-01-15T11:30:00Z" } ``` In case of payment in Euro, the transaction is subject to **Verification of Payee (VoP)**. ## Request VoP **VoP** is a SEPA (Single Euro Payments Area) service that verifies a beneficiary's name against their IBAN to prevent fraud and errors. Before a SEPA Credit Transfer (SCT) or SEPA Instant Transfer (SCT inst) is authorized, the originator's bank will check the provided name and IBAN with the beneficiary's bank and report the result (Match, Close match, or No match) back to the originator. This is a regulatory requirement under the Instant Payments Regulation to enhance security for euro transfers within the SEPA zone. ![VoP flow](/img/bvnk/use-cases/fiat-payout-2.png) When performing VoP, the system may return one of the following results: * **Successful verification**: The beneficiary's name matches. The payment proceeds automatically. * **Partial match**: The provided name is similar to the one associated with the IBAN, but not an exact match. You may still send the payment **at your own risk**, but there is a chance it may not be processed successfully. * **No match**: The beneficiary's name does not match at all. You may still attempt to send the payment, but the likelihood of rejection increases. * **Failed verification** due to other reasons: * For instance, **technical** issues may arise if the verification service is unavailable. * Others, for example, if a beneficiary's bank decides not to conduct verification. All results, except for the successful verification, indicate that if the beneficiary's name in the request doesn't match the name in the beneficiary's bank account, the bank will issue a warning about the inconsistency. In this case, the status 202 is returned. ```json { "id": "UUID", "requiredAction": "CONFIRM_BENEFICIARY", "status": "PAYEE_VERIFICATION_REQUIRED", "verificationResult": { "matchStatus": "PARTIAL_MATCH", "actualName": "George D Collins", // Only present if status is Partial Match "providedName": "George Colline" } } ``` ```json { "id": "UUID", "requiredAction": "CONFIRM_BENEFICIARY", "status": "PAYEE_VERIFICATION_REQUIRED", "verificationResult": { "matchStatus": "NO_MATCH", "providedName": "Gerorge Colline" } } ``` ```json { "id": "UUID", "requiredAction": "CONFIRM_BENEFICIARY", "status": "PAYEE_VERIFICATION_REQUIRED", "verificationResult": { "matchStatus": "TECHNICAL_ERROR", "providedName": "Gerorge Colline" } } ``` ```json { "id": "UUID", "requiredAction": "CONFIRM_BENEFICIARY", "status": "PAYEE_VERIFICATION_REQUIRED", "verificationResult": { "matchStatus": "NOT_POSSIBLE", "providedName": "Gerorge Colline" } } ``` :::warning 🚧 Your transaction is not created yet. Status 202 means that you must send another request to confirm the operation. See Send confirmation below. ::: You can also test how verification works in the sandbox. For that, use the following test names and IBANs, when sending the payment request: * First Name: John Smith * Last Name: Doe * IBAN: MT45SYPL39480744067217548372015 * BIC: SYPLMTM2XXX * First Name: John * Last Name: Doe * IBAN: MT45SYPL39480744067217548372015 * BIC: SYPLMTM2XXX * First Name: Jane * Last Name: Mary * IBAN: MT45SYPL39480744067217548372015 * BIC: SYPLMTM2XXX There are two ways of verifying the beneficiary's name: * As part of the Payout process: by calling another endpoint to confirm the payout operation. See [Confirm payout](#confirm-payout). * As a separate two-step process, where you verify a beneficiary via a separate endpoint and then make the payout using the received `verificationId`. See [Integrate two-step verification](#integrate-two-step-verification). ### Confirm payout You can use this method after making a payout in euros described earlier and receive a response with any of the following `status` values : * `PARTIAL_MATCH` * `NO_MATCH` * `NOT_POSSIBLE` * `TECHNICAL_ERROR` The statuses indicate that the beneficiary's name doesn't match the one associated with the beneficiary's bank account or cannot be verified. If you are aware of this inconsistency and would like to proceed with the payment anyway, send a [`POST /payment/v2/payouts/{id}/actions`](../../../api-explorer/endpoints/payout-confirm-beneficiary-v-2) request with `id` of the transaction received in the 202 response and the following body. ```json Example Payload { "type": "CONFIRM_BENEFICIARY" } ``` In the successful response, you receive the standard payout details, as shown earlier. If the operation is not confirmed within five minutes, it expires. If you provide the wrong transaction ID or send the confirmation request after its expiration, an error is returned. In this case, you will have to create a new payout. ### Integrate two-step verification Alternatively, you can verify the beneficiary's name using the verification endpoint. Note that in case you follow this approach, step 1 of the following procedure must be completed before creating the payout. To verify the beneficiary's name, do the following: 1. Send the [`POST /platform/v1/beneficiaries/verification`](../../../api-explorer/endpoints/verify-beneficiary) request. ```json Example Request { "accountNumber": "GB29MDTR60000131926819", "bankCode": "MDTRGB2L", "firstName": "John", "lastName": "Smith", "businessName": "Acme Corporation Ltd", "currency": "GBP" } ``` In the successful response, you receive the name match status and validation period: ```json Example Response { "id" : "550e8400-e29b-41d4-a716-446655440000", "matchStatus" : "MATCH", "providedName" : "John Smith", "actualName": "J. Smith", // only present for partial match "validUntil" : "2025-01-15T14:30:00Z" // when the verification will expire } ``` 2. Send the [`POST /payment/v2/payouts`](../../../api-explorer/endpoints/payout-create-v-2) request as described earlier. Remember to include the verification `Id`, acquired on step 1 in the response, as the `X-Verification-Id` header. If you provide a valid `verificationId` regardless of status, BVNK verifies that it exists in the records and is not expired, then executes the payment regardless of the `matchStatus` value. The following outcomes are possible: * If the verification session is still valid, the payout will be created, and there will be no need to verify the beneficiary. * If the verification session is expired or `verificationId` is not found within BVNK records, the payout will not be created, and you will receive a `400` error. ## Check payout status To check the status of a payout, send the [`GET /payment/v2/payouts/{transactionReference}`](../../../api-explorer/endpoints/payout-get-v-2) request. In the successful response, you receive the details on the transaction and its status. ```json Response { "transactionReference": "123e4567-e89b-12d3-a456-426614174000", "paymentReference": "Payment for invoice #12345", "amount": { "value": 1000, "currency": "GBP" }, "fee": { "value": 0.00, "currency": "GBP" }, "walletId": "a:24071743003474:xE95Oq7:1", "status": "PROCESSING", "createdAt": "2024-09-26T14:30:00Z", "details": { "type": "FIAT", "beneficiary": { "entityType": "COMPANY", "businessDetails": { "businessName": "Doe Corp" }, "address": { "addressLine1": "123 Main St", "addressLine2": "Apt 4B", "region": "London", "city": "London", "country": "DE", "postCode": "EC2R 8AH", "fullAddress": "123 Main St Suite 500" }, "bankAccount": { "format": "IBAN", "bankName": "Meridian Trust Bank", "accountNumber": "GB29MDTR60000131926819", "bankCode": "MDTRGB2L", "bankAddress": { "addressLine1": "123 Main St", "addressLine2": "Suite 500", "region": "London", "city": "London", "country": "DE", "postCode": "12345", "fullAddress": "123 Main St Suite 500" } }, "correspondentBic": "ABCDEF12" } }, "metadata": { "field1": "field1 value", "field2": "field2 value" } } ``` Also, you can listen to the following webhooks to get notified when the status of the payment changes: * [Fiat payout status change](../../../api-explorer/bvnk-webhooks/payment-payout-status-change-v-2) * [Fiat pay-in status change](../../../api-explorer/bvnk-webhooks/payment-payin-status-change-v-2) --- ## Overview(Virtual-accounts) This guide outlines how to use virtual accounts (VAs) to manage fiat and crypto payments through BVNK. A **wallet** stores value and maintains balances in fiat or crypto. You can connect payment instruments, such as crypto addresses, to a wallet. If crypto is received through a channel linked to a fiat wallet, it is automatically converted and deposited as fiat. When a virtual account is linked to the wallet, you can withdraw funds through the VA. The wallet remains separate from payment instruments, allowing flexible fund management. A **virtual account** is a BVNK-managed payment instrument that facilitates sending and receiving funds. It represents a virtual identifier (vIBAN or ABA) connected to a wallet. VAs are issued in the name of your business or your customers, enabling personalised payment routing. They support SEPA payments in EUR and SWIFT payments in USD, EUR, and GBP for cross-border transfers. To see the schemes available through different jurisdictions, see [Payment schemes](../../../references/payment-methods). :::info This product is also available to businesses in the UK and in supported countries outside the EEA. GBP accounts are limited to companies incorporated in the UK, the Isle of Man, Jersey, Gibraltar, or Guernsey. ::: --- ## For who is this product intended? Virtual accounts are intended for regulated financial institutions and similar businesses that need to manage payments for themselves and their customers. BVNK streamlines this process and provides tools to efficiently manage funds between your business, your customers, and their end-customers. The **Virtual accounts** product supports the following models: - **Direct** intended for business entities, including fintech companies, gaming platforms, crypto businesses, and others. - **Customer virtual accounts** enable regulated financial institutions, such as banks, payment service providers (PSPs), crypto and trading platforms to create underlying virtual accounts for their own customers: - Individuals - Businesses **Virtual accounts** allow you to create payment accounts for your business with unique identifiers, details, and vIBANs issued in your business name. These accounts function like traditional bank accounts for sending and receiving payments but do not support credit facilities. Each vIBAN links directly to your payment account, enabling sending and receiving payments under your business name. You can create crypto and fiat wallets and transfer funds between them. Each crypto payment transaction generates a corresponding fiat transaction record for accounting and reconciliation. Named virtual accounts support compliance, accounting, and tax reporting by providing clear transaction records that show you are moving your own funds. This structure allows you to separate business operations or projects while maintaining full control over your funds. **What is a standard use case for virtual accounts?** > You operate a crypto trading platform that receives deposits in stablecoins. Your business needs to convert these stablecoin deposits to fiat currency and withdraw funds to your external bank account for operational expenses, supplier payments, or regulatory requirements. > > With a virtual account, you receive stablecoin deposits into your crypto wallet. You convert the stablecoins to euros through BVNK's trading functionality, which deposits euros into your fiat wallet. The conversion creates a crypto transaction record and a corresponding fiat transaction record for accounting. You then withdraw euros to your external bank account using the vIBAN linked to your virtual account. > > This workflow lets you manage crypto-to-fiat conversions while maintaining clear transaction records for compliance and accounting. The named vIBAN ensures withdrawals are clearly attributed to your business, supporting audit trails and tax reporting. The **customer virtual accounts** product provides the infrastructure to create virtual accounts for your customers, simplifying payment tracking and management. To manage transactions for your customers and their clients, whether businesses or individuals, you assign each customer a virtual account with a unique name, details, and a vIBAN. These vIBANs link to your payment account, allowing you to send and receive payments on behalf of your customers under their names or transfer funds between accounts. Customer virtual accounts are **EUR-only** and support euro pay-ins and payouts via SEPA. They do **not** offer crypto capabilities. Your customers cannot hold, send, receive, or convert crypto through their virtual accounts. Crypto capabilities are available only at your principal account level. BVNK offers virtual accounts as an optional solution. You and your customers may use them, but they are not required. Other BVNK services, such as payments, crypto trading, or treasury management, are available without setting up or managing virtual accounts. To qualify for customer virtual accounts, your business must: - Be a regulated payment service provider or financial institution, for example, holding a UK FCA license. - Demonstrate a robust financial crime control framework (AML, risk policies, transaction monitoring, etc.). - Undergo compliance checks, including documentation review and interviews with BVNK's compliance team. **How onboarding responsibilities are shared** You onboard with BVNK as the principal customer and perform KYB and KYC checks on your underlying customers. BVNK does not onboard your underlying customers; they do not undergo additional KYC with BVNK unless flagged during screening. BVNK continues to screen and monitor underlying customers through its compliance team. ![Compliance model for customer virtual accounts: BVNK contracts with the principal customer, who holds their own license and carries out KYB and KYC on their underlying customers. BVNK has no contractual relationship with the underlying customer.](/img/bvnk/use-cases/va/customer-va-compliance-model.svg) **What is a standard use case for customer virtual accounts?** VAs are designed for partners who need to segregate funds, simplify reconciliation, or embed financial services within their platforms. Virtual accounts support a range of payment scenarios: **For Crypto and Trading Platforms**: > You represent a crypto trading platform and want to allow your customers to deposit fiat funds for crypto trading. Your platform receives deposits from hundreds of customers daily, and you need a robust payment reconciliation mechanism to match incoming payments to the correct customer accounts. > > With virtual accounts, each of your customers receives a unique named vIBAN. When a customer deposits €5,000 into their virtual account, you can immediately identify the customer who made the payment using the unique account details. The funds are deposited into your wallet, and you can automatically credit the customer's trading account balance. Any crypto trading or conversion then happens at your own principal account level, where crypto capabilities are available—the customer's virtual account itself holds only euros. > > This setup enables seamless fund segregation, accurate reconciliation, and a smooth user experience. **For Payment Service Providers and Financial Institutions**: > You represent a Payment Service Provider serving multiple business customers. Your customers need to receive payments from their own clients and make payouts to suppliers, employees, or contractors. Each customer requires their own named IBAN to maintain clear payment separation and reconciliation. > > You create a virtual account for each of your customers with a unique vIBAN issued in their business name. When **customer A** receives a €10,000 payment from their client, the funds are automatically routed to customer A's virtual account, and you can immediately identify and reconcile the payment. Customer A can then use those funds to make payouts to suppliers or employees, all processed through their dedicated virtual account. > > This model allows you to process third-party payments on behalf of your customers while maintaining complete fund segregation, enabling accurate reconciliation, and providing your customers with professional payment infrastructure. At your principal account level, you can optionally use stablecoin pre-funding for faster settlement and cross-border capabilities, while your customers' virtual accounts remain euro-only. **Additional use cases**: Virtual accounts also support the following scenarios: - **Neobanks**: Issue named IBANs to customers for personal or business banking services - **Treasury management**: Segregate funds across business units or projects using dedicated virtual accounts --- ## How to get started? During onboarding, BVNK learns your use cases, currencies, and geographies, then enables virtual accounts that match your business, vertical, and region. If you need more features later, such as support for additional currencies, contact support to enable virtual accounts in EUR, GBP, or USD. Once a virtual account is enabled, you can create it through the API or BVNK Portal. For the steps, see [Set up wallets](../../../get-started/set-up-wallets). Virtual accounts require approval from BVNK's compliance team. To qualify, your business must: - Be a fully licensed financial services provider, for example, have a UK FCA license. - Demonstrate a robust financial crime control framework, including AML policies, risk management, and transaction monitoring. - Complete compliance checks, including documentation review and interviews. Your business must be approved for the customer virtual accounts solution before you can request or create virtual accounts. To request virtual accounts, follow these steps: 1. **Contact BVNK** through your Account Manager or sales contact to express interest in the VA product. 2. **Submit required documentation**: - Proof of regulatory status, for example, FCA license, MSB registration - AML/CTF policies and procedures - Risk management framework - Details of your business model and use case BVNK's compliance team reviews your documents and may request additional information. Enhanced Due Diligence (EDD) may be required, especially if your business operates in a higher-risk sector, such as gambling, gaming, or trading. After BVNK's compliance team approves your application, your account will be configured for the use of a customer VA. After that, you are ready to [integrate customer VA to your platform](../provide-customer-virtual-accounts). --- **Ready to start?** After completing the setup process with BVNK, select the model that fits your business needs and proceed with integration: - [Virtual accounts for your business](./prepare-for-va-integration.mdx) - [Provide virtual accounts to your customers](./provide-customer-virtual-accounts.mdx) --- ## Enable Express wallets for customers Express Embedded offers a customizable user interface and backend APIs that add stablecoin wallet functionality to your platform. This allows you to provide stablecoin storage and management to your customers with minimal investment and development effort. Embedded Express is intended for customers who need wallets and stored balances. It is best suited for stored-value scenarios where customers maintain balances over time. For one-time payments or payments not requiring a stored-value wallet (e.g., on-ramps or off-ramps to an external wallet or account), we recommend using our [Embedded Custom model](../../../get-started/delivery-models/?delivery-model=custom-embedded). This product lets you use your existing pre-funded fiat balances to offer stablecoin storage services. Your customers do not need to interact with crypto providers or manage conversions. All Embedded Express wallets include built-in capabilities: - **Rewards**: All balances can earn rewards automatically. - **Spending cards**: All your customers can be issued with a spending card. These features are enabled by default and require no additional setup. Express provides revenue streams for partners: - **Reward income sharing**: As a partner, you can receive a share of the reward income generated by customer balances. You may choose to: - Pay all rewards to your customers. - Keep all rewards for your platform. - **Card interchange income**: You can earn revenue from each card transaction made by your customers. - **Wallet activity markups**: You can add markups to, and earn revenue on, wallet activity (payments, conversions, etc.) by your customers as they use their stablecoin wallets. Contact your Account Manager to discuss available revenue streams, fees, and revenue-sharing options with Embedded Express. ## For who is this product intended? This product is designed for any BVNK partner looking to provide stablecoin wallets and associated services to their customers, for example: - **Payment Service Providers (PSPs)** looking to offer stablecoin storage to customers with pre-funded fiat balances - **Platforms and marketplaces** needing to convert and store customer funds in stablecoins - **Neobanks and financial services** aiming to provide stablecoin wallet functionality to users --- ## What is a standard use case? > You represent a platform (for example, **RentPlatform**) and one of your customers has $100,000 in their account. You want to offer this customer a branded stablecoin wallet to store and manage their funds. > > After your platform onboards with BVNK, you gain access to a fiat wallet for depositing funds. When the customer opts for the stablecoin wallet, you convert funds from the fiat wallet to stablecoins and store them in the customer's wallet. The customer can then view their balance, make transactions, and manage their spending card through BVNK's wallet interface, which is branded with RentPlatform's colors and logos. --- ## Supported payment types BVNK supports multiple conversion options to address different business needs: - **Fiat to stablecoins**: Convert fiat currency to stablecoins and store them in customer wallets. - **Crypto to crypto**: Convert from one stablecoin to another within customer wallets. --- ## How does it work? The Embedded Express model automates the interface shown to a customer: onboarding, registration, login. Your platform needs to call a single API endpoint any time your customer wants to access the platform, regardless of their status: new or existing. BVNK will determine the appropriate action and return the relevant link. To enable Express capabilities for your customers, follow these steps: - [**Choose onboarding method**](#choose-onboarding-method): Enable your customers to be onboarded by BVNK, either through Express UI or by passing KYC documentation directly. - [**Get Express session link**](#get-express-session-link): Enable your customers to access their stablecoin wallet by providing a redirect URL. - [**Fund wallets**](#fund-wallets): Let your customers fund their wallet by on-ramping from fiat held in their account on your platform. ### Choose onboarding method When onboarding a new customer, you can choose to apply the Express onboarding process or your own onboarding flow. - **Express onboarding**: Direct new customers to the Express UI where they will be onboarded by BVNK. This option is recommended if you want to launch quickly or do not manage customer onboarding, such as if your business is unregulated or does not hold a license. - **Your own onboarding flow**: Continue using your existing onboarding process and endpoints for embedded customers. You only need to [implement the existing APIs](../../../get-started/create-embedded-partner-customer/onboard-epc/). This option is recommended if you are licensed business and maintain your own KYC data. :::warning For your own onboarding flow, you must implement the [hosted agreements page](../../../get-started/create-embedded-partner-customer/signing-customer-agreements/#sign-agreements-via-hostedurl). ::: The system manages different customer scenarios: | If your customer is | Then, you will receive | | --------------------------------------- | ------------------------------------------------------------------------------ | | **new** | an onboarding link. | | **existing and has never used Express** | an Express registration link | | **existing Express customer** | a login link to the Express app | | **has an active session token** | a redirect link so that customer can access the wallet interface without login | See below for usage of the get session link endpoint—the same endpoint used for onboarding. ### Get Express session link To get a wallet session URL for your customer, send the [`POST /platform/v1/express-sessions`](../../../api-explorer/endpoints/create-express-session) request with the following body parameters: For customers who already exist in your system, use their customer reference: ```json { "customerReference": "22c8168d-f735-43f8-9a3b-a1578efeb86c", // optional "theme": { "logo": "https://ok14static.oktacdn.com/fs/bco/1/fs0waisynotIeKa80697", "primary": "#121212", "accent": "#DEF832", "background": "#382457" } } ``` For new customers, provide their details: ```json { "type": "INDIVIDUAL", "countryCode": "DE", "externalCustomerId": "22c8168d-f735-43f8-9a3b-a1578efeb86n", // optional "theme": { "logo": "https://ok14static.oktacdn.com/fs/bco/1/fs0waisynotIeKa80697", "primary": "#121212", "secondary": "#382457", "accent": "#DEF832" } } ``` The `theme` parameter is optional and lets you customize the Express interface to align with your brand. | Parameter | Description | | ------------ | --------------------------------------------------------------------------------------------------- | | `logo` | URL to your company logo. For example, https://ok14static.oktacdn.com/fs/bco/1/fs0waisynotIeKa80697 | | `primary` | Primary brand color as the hex code. For example, #121212 | | `accent` | Accent color as the hex code. For example, #DEF832 | | `background` | Background color as the hex code. For example, #382457 | | `secondary` | Secondary color as the hex code. For example, #382457 | Express uses these colors to generate a palette that reflects your brand and displays your logo throughout the interface. In the response, you receive the `walletSessionUrl`. ```json { "walletSessionUrl": "https://wallet.staging.bvnk.com/welcome?sessionId=f5b2d8cd-887d-421c-ae00-9d3f82b5a45f" } ``` ### Fund wallets See [Store funds in your wallet](./store-funds.mdx) for more information on how to fund your customers' wallets. ### User experience Share the `walletSessionUrl` with your customer using a web view, redirect, or other integration method. After authentication, customers access their wallet home screen, where they can: - View their balance - See transaction history - View on-ramp payments from your platform - View outbound payments - Manage their spending card --- **Ready to start?** After completing the prerequisites above, you can proceed with the integration. The following sections will guide you through: - [Fund wallets](./store-funds.mdx): Let the end user fund their wallet by on-ramping from fiat held in their account on your platform --- ## Fund Express wallets This guide explains how to debit funds from your fiat account and credit them to your customer's stablecoin account. To complete the conversion, you will need the recipient's stablecoin wallet ID, which you can obtain using the get wallets endpoint. The Embedded Express product follows a straightforward process: 1. **Get wallet ID**: Retrieve the customer's stablecoin wallet ID using the get wallets endpoint. 2. **Create quote**: Generate a conversion quote from your fiat wallet to the customer's stablecoin wallet. 3. **Accept quote**: Accept the quote to execute the conversion. This action debits your fiat account and credits the customer's stablecoin wallet. Once complete, the converted stablecoins are stored in the customer's wallet and available for future transactions. ## Prerequisites Before initiating the transfer, ensure you have the following: - A fiat wallet with a sufficient balance - The `customerReference` for the end user whose stablecoin wallet you intend to credit - API credentials with merchant-level permissions You do not need to create a wallet for Express customers. USDC wallets are automatically created when customers log into the Express app for the first time. ## Get wallets To obtain the wallet ID for the end user's stablecoin account, use the [`GET /ledger/v1/wallets`](../../../api-explorer/endpoints/ledger-wallet-list) endpoint. Filter wallets by `customerReference` to locate a specific customer's wallets and by `currencyCode` to find wallets in a particular currency, such as `USDC` or `USDT`. ```cUrl Request GET /ledger/v1/wallets?customerReference=33e85982-e22b-4e8b-9057-445cf000bea9¤cyCode=USDC ``` The response provides a list of wallets and their details: ```json Response { "content": [ { "id": "a:24092328494070:G5i4XZ9:1", "accountReference": "4488d086-f2c2-5bdf-0b01-7dgcedfbfbb0", "customerReference": "33e85982-e22b-4e8b-9057-445cf000bea9", "name": "USDC Crypto Wallet", "status": "ACTIVE", "balance": { "value": 250.00, "currencyCode": "USDC" }, "ledgers": [ { "type": "CRYPTO", "address": "0x1234567890abcdef1234567890abcdef12345678", "network": "ETHEREUM" } ], "capabilities": "SAFEGUARDED" } ], ... } ``` Find the `id` field in the response. This is the wallet ID required for the transfer. ## Fund wallet 1. Create a quote by sending the [`POST /api/v1/quote`](../../../api-explorer/endpoints/quote-create) request. ```json { "from": "ETH", "to": "USDC", "fromWalletLsid": "a:26012726855253:zMpcRHg:1", "useMaximum": false, "useMinimum": false, "reference": "REF6435921392", "toWalletLsid": "a:26012659807080:SsaiV4j:1", "amountIn": "0.004", "payInMethod": "wallet", "payOutMethod": "wallet" } ``` For detailed request parameters, refer to the [Create Quote](../../../api-explorer/endpoints/quote-create) endpoint documentation. You will receive a response with the quote price, applicable fees, and the quote UUID. ```json Example Response { ... "quoteUuid": "019bfe65-92d8-7738-b855-1734474b279f", "price": "0.004", "fees": "0.0004", "quoteStatus": "PENDING", "paymentStatus": "PENDING" ... } ``` :::note Many customers prefer a one-step flow rather than creating a quote and then accepting it separately. If you want to explore auto-accepting quotes via the quote endpoint, contact BVNK support or check the [Create Quote](../../../api-explorer/endpoints/quote-create) endpoint documentation for available options. ::: 2. Accept the quote by sending the [`PUT /api/v1/quote/{uuid}`](../../../api-explorer/endpoints/quote-accept) request with the quote UUID received in the previous response. ```curl Example Request curl -X PUT https://api.staging.bvnk.com/api/v1/quote/accept/019bfe65-92d8-7738-b855-1734474b279f ``` :::warning You may choose whether or not to display the rate to your customers, but you must **not** present it as a guaranteed rate. The rate must be shown as indicative only, reflecting the nature of the service BVNK provides to that user. ::: After you accept the quote, the stablecoins are credited to the user's wallet immediately. ## Get wallet balance For the best user experience, we recommend implementing functionality to retrieve wallet balances. Integrate the [Get Wallet](../../../api-explorer/endpoints/wallet-read) or [List Wallet Balances](../../../api-explorer/endpoints/wallet-balance-list) endpoints to display real-time balances to users on your platform. This allows users to: - View the current balances of their wallets (including available and pending amounts). - Confirm that funds have arrived before starting transfers or conversions. - Track their assets across multiple currencies or wallets. - Increase trust and transparency in your application. ## Receive notifications Use the [Webhooks](../../../get-started/create-webhook-listener) feature to receive notifications about wallet balance changes. When the wallet balance changes, you will receive a webhook notification with the following details: - The wallet ID - The new balance - The currency - The timestamp - The transaction ID --- ## Tutorial Intro Let's discover **Docusaurus in less than 5 minutes**. ## Getting Started Get started by **creating a new site**. Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**. ### What you'll need - [Node.js](https://nodejs.org/en/download/) version 18.0 or above: - When installing Node.js, you are recommended to check all checkboxes related to dependencies. ## Generate a new site Generate a new Docusaurus site using the **classic template**. The classic template will automatically be added to your project after you run the command: ```bash npm init docusaurus@latest my-website classic ``` You can type this command into Command Prompt, Powershell, Terminal, or any other integrated terminal of your code editor. The command also installs all necessary dependencies you need to run Docusaurus. ## Start your site Run the development server: ```bash cd my-website npm run start ``` The `cd` command changes the directory you're working with. In order to work with your newly created Docusaurus site, you'll need to navigate the terminal there. The `npm run start` command builds your website locally and serves it through a development server, ready for you to view at http://localhost:3000/. Open `docs/intro.md` (this page) and edit some lines: the site **reloads automatically** and displays your changes. --- ## Beneficiary ### Term explanation The entity that receives the transaction. --- ## Originator ### Term explanation The entity that initiates the transaction. For example, a customer that wants to buy crypto with fiat currency.