# 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. ## 2025-08-30 Technical changes and notices. - API response fields: additive fields may be introduced without prior notice. Ensure deserializers ignore unknown keys to avoid runtime errors. - Deprecation: `transactions[].risk` data remains unavailable; clients should not rely on this field. - Channels redirect: migration of `redirectUrl` from `business.coindirect.com` to `pay.bvnk.com` is complete; ensure any allowlists are updated. - CSV export: wallet transaction CSV exports now support both preset ranges and custom date ranges via the portal. Reference: `https://docs.bvnk.com/changelog#/` --- ## 2025-09-12 Highlights from the BVNK change log. - Channels `redirectUrl` migrating from `business.coindirect.com` to `pay.bvnk.com` (Nov 2023) - JSON deserialization notice: clients should tolerate additive fields - `transactions[].risk` data deprecated from 2023-10-01 - Export wallet transactions to CSV by period or date range - Improved payment status labels: Late, Underpaid, Overpaid Source: `https://docs.bvnk.com/changelog#/` --- ## Release notes This section contains release notes and change history for the BVNK API and portal. --- ## BVNK Main API Welcome to the BVNK Main API documentation. This API enables seamless and secure transactions, including payments, channels, and digital wallet operations. ## Getting Started Before you begin, make sure you have: - Created an account on the [BVNK Merchant Portal](https://app.sandbox.bvnk.com) - Generated your API keys (Hawk Auth ID and Secret Key) - Reviewed the authentication requirements ## API Environments - **Production**: `https://api.bvnk.com` - **Sandbox**: `https://api.sandbox.bvnk.com` ## Overview Articles - [Overview](/bvnk/api-explorer/api-overview/overview) - [Authentication](/bvnk/api-explorer/api-overview/authentication) - [Errors](/bvnk/api-explorer/api-overview/errors) - [Idempotency](/bvnk/api-explorer/api-overview/idempotency) - [Metadata](/bvnk/api-explorer/api-overview/metadata) - [Pagination](/bvnk/api-explorer/api-overview/pagination) ## API Endpoints For detailed API endpoint documentation, see the [API Reference](/api/bvnk-main-api). --- ## Authentication Most interactions with the BVNK Crypto Payments API require the use of the API keys. Before moving forward, make sure you have already created your `Hawk Auth ID` and `Hawk Auth Key` as described in [Generate API Keys](../../../get-started/generate-api-keys), as you won't be able to progress without them. ## HAWK Authentication BVNK employs Hawk Authentication to ensure the authenticity of requests made to our APIs. The [Hash-based Message Authentication Code (HMAC)](https://www.okta.com/identity-101/hmac/) is calculated using SHA256. While HAWK supports optional payload validation for POST and PUT data, along with response payload validation, these features are not activated for the BVNK API and can be disregarded. Below, you will find code examples for the most commonly used programming languages. :::warning These snippets are ready to use but to reflect your specific requirements, adjust the following: * `method` * `url` * `Hawk Auth ID` * `Hawk Auth Key` ::: ```java /** * Main class demonstrates the generation of a Hawk authentication header. */ public class Main { // The credentials used to generate the Auth headers static String id = "<>"; static String key = "<>"; static String algorithm = "HmacSHA256"; // The hashing algorithm used. // The request to be performed static String method = "GET"; static String url = "https://api.sandbox.bvnk.com/api/v1/merchant"; public static void main(String[] args) throws Exception { String header = hawkHeader(url, method, id, key, algorithm); System.out.println(header); } /** * This method generates a timestamp of the current time in seconds * * @return A timestamp in seconds */ public static long getTimestampInSeconds() { return System.currentTimeMillis() / 1000; } /** * This method creates a string of random alphanumeric characters * * @param length The required length of the string * @return A string of random characters */ public static String generateNonce(int length) { String possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; StringBuilder text = new StringBuilder(length); Random rnd = new SecureRandom(); for (int i = 0; i < length; i++) { text.append(possible.charAt(rnd.nextInt(possible.length()))); } return text.toString(); } /** * This method generates a normalized string out of the header options * * @param type The request type * @param ts The timestamp in seconds * @param nonce The generated nonce * @param method The HTTP method of the call * @param url The URL of the request * @param host The host * @param port The port * @param hash The hash * @return A normalized string containing all parts to be hashed */ public static String generateNormalizedString(String type, long ts, String nonce, String method, String url, String host, String port, String hash) { String headerVersion = "1"; return "hawk." + headerVersion + "." + type + "\n" + ts + "\n" + nonce + "\n" + method.toUpperCase() + "\n" + url + "\n" + host.toLowerCase() + "\n" + port + "\n" + hash + "\n" + "\n"; } /** * This method generates the request Message Authentication Code (MAC) using the MAC-SHA256 hashing algorithm * * @param type The type to generate * @param key The key used to generate the Auth header * @param options The options used to generate the code * @return a request MAC */ public static String generateRequestMac(String type, String key, String options) { byte[] hmacSha256 = HmacUtils.hmacSha256(key, options); return Base64.getEncoder().encodeToString(hmacSha256); } /** * This method generates the Hawk authentication header * * @param uri A full URI string * @param method The HTTP method (GET, POST, etc.) * @param id The ID from the credentials * @param key The key from the credentials * @param algorithm The algorithm from the credentials * @throws URISyntaxException Will throw an error if the provided URI string is of an invalid type. * @return The Hawk authorization header */ public static String hawkHeader(String uri, String method, String id, String key, String algorithm) throws URISyntaxException { // Application time long timestamp = getTimestampInSeconds(); // Generate the nonce String nonce = generateNonce(6); // Parse URI URI parsedUri = new URIBuilder(uri).build(); String host = parsedUri.getHost(); String port = String.valueOf(parsedUri.getPort() == -1 ? (parsedUri.getScheme().equals("http") ? 80 : 443) : parsedUri.getPort()); String resource = parsedUri.getPath() + (parsedUri.getQuery() != null ? "?" + parsedUri.getQuery() : ""); // Prepare artifacts object to be used in MAC generation String options = generateNormalizedString("header", timestamp, nonce, method, resource, host, port, ""); // Generate the MAC String mac = generateRequestMac("header", key, options); // Construct Authorization header return "Hawk id=\"" + id + "\", ts=\"" + timestamp + "\", nonce=\"" + nonce + "\", mac=\"" + mac + "\""; } } ``` ```csharp using System; using System.Security.Cryptography; using System.Text; using System.Linq; public class MainClass { // The credentials used to generate the Auth headers private static string id = "<>"; private static string key = "<>"; private static string algorithm = "HMACSHA256"; // The request to be performed private static string method = "GET"; private static string url = "https://api.sandbox.bvnk.com/api/v1/merchant"; public static void Main(string[] args) { try { string header = HawkHeader(url, method, id, key, algorithm); Console.WriteLine(header); } catch (Exception e) { Console.Error.WriteLine("Error occurred:"); Console.Error.WriteLine(e); } } /// /// Generates a timestamp of the current time in seconds. /// /// A timestamp in seconds. public static long GetTimestampInSeconds() { DateTimeOffset dto = DateTimeOffset.Now; return dto.ToUnixTimeSeconds(); } /// /// Creates a string of random alphanumeric characters. /// /// The required length of the string. /// A string of random characters. public static string GenerateNonce(int length) { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; var random = new Random(); return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray()); } /// /// Generates a normalized string out of the header options. /// /// A normalized string containing all parts to be hashed. public static string GenerateNormalizedString(string type, long ts, string nonce, string method, string url, string host, string port, string hash) { string headerVersion = "1"; return $"hawk.{headerVersion}.{type}\n{ts}\n{nonce}\n{method.ToUpper()}\n{url}\n{host.ToLower()}\n{port}\n{hash}\n\n"; } /// /// Generates the request Message Authentication Code (MAC). /// /// A request MAC generated from the HMAC-SHA256 hashing algorithm. public static string GenerateRequestMac(string type, string key, string options) { using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)); byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(options)); return Convert.ToBase64String(hashBytes); } /// /// Generates the hawk authorization header. /// /// A Hawk authorization header including the Hawk Auth ID, timestamp, nonce, and MAC. public static string HawkHeader(string uriStr, string method, string id, string key, string algorithm) { long timestamp = GetTimestampInSeconds(); string nonce = GenerateNonce(6); var uri = new UriBuilder(uriStr); string host = uri.Host; string port = uri.Port.ToString(); string resource = uri.Path + (uri.Query.Length > 0 ? uri.Query : ""); string options = GenerateNormalizedString("header", timestamp, nonce, method, resource, host, port, ""); string mac = GenerateRequestMac("header", key, options); return $"Hawk id=\"{id}\", ts=\"{timestamp}\", nonce=\"{nonce}\", mac=\"{mac}\""; } } ``` ```javascript const Hawk = require("hawk"); /** * The method to generate the hawk auth header * @param uri The full uri of the request * @param method The request method used * @param options The request options including credentials * @returns a Hawk authorization header including the Hawk Auth ID, time stamp, nonce, and MAC */ function hawkHeader(url, method, options) { const { header } = Hawk.client.header(url, method, options); return header; } // The credentials used to generate the Auth headers const credentials = { id: "<>", key: "<>", algorithm: "sha256", // The hashing algorith used. }; // The request to be performed const request = { method: "GET", url: "/api/v1/merchant", host: "https://api.sandbox.bvnk.com", }; console.log( hawkHeader(`${request.host}${request.url}`, request.method, { credentials, }) ); ``` ```php $timestamp, 'nonce' => generateNonce(6), 'method' => $method, 'resource' => parse_url($uri, PHP_URL_PATH) . (parse_url($uri, PHP_URL_QUERY) ? '?' . parse_url($uri, PHP_URL_QUERY) : ''), 'host' => parse_url($uri, PHP_URL_HOST), 'port' => parse_url($uri, PHP_URL_PORT) ?? (parse_url($uri, PHP_URL_SCHEME) === "http" ? 80 : 443), ]; // Generate the MAC $mac = generateRequestMac("header", $credentials, $artifacts); // Construct Authorization header $header = 'Hawk id="' . $credentials['id'] . '", ts="' . $artifacts['ts'] . '", nonce="' . $artifacts['nonce'] . '", mac="' . $mac . '"'; return $header; } // The credentials used to generate the Auth headers $credentials = [ 'id' => "<>", 'key' => "<>", 'algorithm' => "sha256", // The hashing algorithm used. ]; // The request to be performed $request = [ 'method' => "GET", 'url' => "/api/v1/merchant", 'host' => "https://api.sandbox.bvnk.com", ]; // Print Hawk Auth header echo hawkHeader($request['host'] . $request['url'], $request['method'], ['credentials' => $credentials]); ?> ``` ```ruby require 'openssl' require 'base64' require 'uri' # Generates a timestamp of the current time in seconds # @return [Integer] A timestamp in seconds def get_timestamp_in_seconds Time.now.to_i end # Creates a string of random alphanumeric characters # @param length [Integer] The required length of the string # @return [String] A string of random characters def generate_nonce(length) possible = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a (0...length).map { possible[rand(possible.length)] }.join end # Generates a normalized string out of the header options # @param type [String] The request type # @param options [Hash] The options hash # @return [String] A normalized string containing all parts to be hashed def generate_normalized_string(type, options) header_version = "1" normalized = "hawk." + header_version + "." + type + "\n" + options[:ts].to_s + "\n" + options[:nonce] + "\n" + (options[:method] || "").upcase + "\n" + (options[:resource] || "") + "\n" + options[:host].downcase + "\n" + options[:port].to_s + "\n" + (options[:hash] || "") + "\n" normalized += "\n" normalized end # Generates the request Message Authentication Code (MAC) using the MAC-SHA256 hashing algorithm # @param type [String] The type to generate # @param credentials [Hash] The credentials used to generate the Auth header # @param options [Hash] The options used to generate the code # @return [String] a request MAC def generate_request_mac(type, credentials, options) normalized = generate_normalized_string(type, options) hmac = OpenSSL::HMAC.new(credentials[:key], credentials[:algorithm]) hmac.update(normalized) Base64.strict_encode64(hmac.digest) end # Generates the Hawk authentication header # @param uri [String] A full URI string # @param method [String] The HTTP method (GET, POST, etc.) # @param options [Hash] Hash containing credentials and other options # @raise [RuntimeError] If the provided arguments are of an invalid type. # @return [String] The Hawk authorization header def hawk_header(uri, method, options) uri = URI(uri) if uri.is_a? String timestamp = get_timestamp_in_seconds credentials = options[:credentials] if credentials.nil? || credentials[:id].nil? || credentials[:key].nil? || credentials[:algorithm].nil? raise "Invalid credentials" end artifacts = { ts: timestamp, nonce: generate_nonce(6), method: method, resource: uri.path + (uri.query.nil? ? '' : "?#{uri.query}"), host: uri.host, port: uri.port || (uri.scheme == "http" ? 80 : 443) } mac = generate_request_mac("header", credentials, artifacts) header = "Hawk id=\"#{credentials[:id]}\", ts=\"#{artifacts[:ts]}\", nonce=\"#{artifacts[:nonce]}\", mac=\"#{mac}\"" header end credentials = { id: "<>", key: "<>", algorithm: "sha256", } request = { method: "GET", url: "/api/v1/merchant", host: "https://api.sandbox.bvnk.com", } header = hawk_header("#{request[:host]}#{request[:url]}", request[:method], { credentials: credentials }) puts header ``` ```python # This function generates a timestamp of the current time in seconds def get_timestamp_in_seconds(): return int(time.time()) # This function creates a string of random alphanumeric characters def generate_nonce(length): return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(length)) # This function generates a normalized string out of the header options def generate_normalized_string(type, options): header_version = "1" normalized = f'hawk.{header_version}.{type}\n{options["ts"]}\n{options["nonce"]}\n{options["method"].upper()}\n{options["resource"]}\n{options["host"].lower()}\n{options["port"]}\n{options["hash"]}\n\n' return normalized # This method generates the request Message Authentication Code (MAC) using the MAC-SHA256 hashing algorithm def generate_request_mac(type, credentials, options): normalized = generate_normalized_string(type, options) hmac_obj = hmac.new(credentials['key'].encode(), normalized.encode(), hashlib.sha256) return base64.b64encode(hmac_obj.digest()).decode() # This method generates the Hawk authentication header def hawk_header(uri, method, options): # Validate inputs if not uri or not isinstance(uri, str) or not method or not isinstance(method, str) or not options or not isinstance(options, dict): raise ValueError("Invalid argument type") # Application time timestamp = get_timestamp_in_seconds() # Validate credentials credentials = options['credentials'] if not credentials or not credentials['id'] or not credentials['key'] or not credentials['algorithm']: raise ValueError("Invalid credentials") # Parse URI if it is a string parsed_uri = urllib.parse.urlparse(uri) # Prepare artifacts object to be used in MAC generation artifacts = { 'ts': timestamp, 'nonce': generate_nonce(6), 'method': method, 'resource': parsed_uri.path + parsed_uri.query, # Maintains trailing '?' 'host': parsed_uri.hostname, 'port': parsed_uri.port if parsed_uri.port else (80 if parsed_uri.scheme == 'http' else 443), 'hash': '' } # Generate the MAC mac = generate_request_mac('header', credentials, artifacts) # Construct Authorization header header = f'Hawk id="{credentials["id"]}", ts="{artifacts["ts"]}", nonce="{artifacts["nonce"]}", mac="{mac}"' return header # The credentials used to generate the Auth headers credentials = { 'id': "<>", 'key': "<>", 'algorithm': "sha256", # The hashing algorithm used. } # The request to be performed request = { 'method': "GET", 'url': "/api/v1/merchant", 'host': "https://api.sandbox.bvnk.com", } print(hawk_header(f'{request["host"]}{request["url"]}', request["method"], {'credentials': credentials})) ``` ```node const crypto = require("crypto"); const generateHawkAuthorization = (url, method, hawkId, hawkSecret) => { // Generate Hawk authorization header const timestamp = Math.floor(Date.now() / 1000); const nonce = Math.random().toString(36).substring(7); const parsedUrl = new URL(url); const path = parsedUrl.pathname + parsedUrl.search; const host = parsedUrl.host; const port = parsedUrl.port || (parsedUrl.protocol === 'https:' ? '443' : '80'); // Create normalized request string const normalized = `hawk.1.header\n${timestamp}\n${nonce}\n${method}\n${path}\n${host}\n${port}\n\n\n`; // Generate MAC const mac = crypto.createHmac('sha256', hawkSecret) .update(normalized) .digest('base64'); // Format Hawk header return `Hawk id="${hawkId}", ts="${timestamp}", nonce="${nonce}", mac="${mac}"` } ``` :::warning The HTTP method and endpoint URL significantly influence the generation of the correct hash values in your authorization header. It's crucial to ensure these values are dynamically updated to align with your actual requests. Otherwise, your requests may fail. ::: --- ## Errors The BVNK API responds with detailed information in the event of a failed request. Below is a comprehensive list of payment error codes, along with brief descriptions on how to address them. This resource serves as a valuable reference for developers, enabling them to efficiently troubleshoot and address errors in their applications' integration with BVNK's API. The errors are returned in the following format: ```json Example Response for Payouts { "code": "bvnk:payment:0005", "status": "Bad Request", "message": "Beneficiary details validation failed", "details": { "documentLink": "../api-overview/errors", "errors": { "requestBody": [ "Address country required" ] } } } ``` ```json Array of Errors { "requestId": "cd8b277efeeccdea4067a4030ecc4e45", "errorList": [ { "requestId": null, "code": "MER-PAY-2012", "parameter": "amount", "message": "insufficient funds" } ] } ``` Here, * `code`: Error code that identifies the specific error type. See the list of codes and descriptions below. * `status`: HTTP status associated with the error, such as`400`, `403`. * `message`: Human-readable explanation of the error and guidance on how to resolve it. In most cases, error details are propagated into this field (often similar to `details.error`). * `details`: Structured information about why the request failed. This field is most common for Bad Request (400) errors, but it can be null when the details have been propagated to the message or are otherwise unavailable. For example, the above payout response appears if you specify `countryCode: null` in the request. The `requestBody` contains specific information about the missing field. * `details.documentLink`: Link to this article where you can learn more about the caught error. ## 0001-0099 ### Fiat payouts | **Code** | **Message** | **Description** | | :-------: | :---------- | :----------------- | | BVNK:PAYMENT:0001 | Received Bad Request | The system received a request with invalid parameters or missing required information. Check the request format, parameters, and ensure all required fields are correctly filled. | | BVNK:PAYMENT:0002 | Payout with the given identifier could not be found | The system was unable to locate the requested payout using the provided ID. Verify that the payout ID is correct and exists in the system. | | BVNK:PAYMENT:0003 | Forbidden → received request with wrong authentication details | The request was rejected due to an authentication failure. Ensure your API keys, tokens, or credentials are valid and correctly included in the request header. | | BVNK:PAYMENT:0004 | Error while receiving or validating beneficiary | The beneficiary details could not be retrieved, or failed validation checks. This may occur due to missing or malformed fields (name, address, account identifiers), failed KYC/KYB or sanctions screening, or temporary downstream connectivity errors. Verify beneficiary data to ensure the required fields and formats (e.g., IBAN/ABA) are accurate. Confirm the beneficiary is allowed, and retry once issues are resolved. | | BVNK:PAYMENT:0005 | Validation of the beneficiary has failed | The beneficiary did not pass mandatory validation rules. Typical reasons include invalid account identifiers, an unsupported destination, a mismatched currency/network, or a compliance screening failure. Correct the beneficiary information (names, address, account numbers, tags/memos where required) or select a different, validated beneficiary before retrying. | | BVNK:PAYMENT:0006 | Payout with the idempotency key already exists | A payout using the provided idempotency key already exists. For idempotent safety, the platform rejects creating a new payout with the same key. If you intend to retry the same request, use the original response; otherwise, generate a new, unique idempotency key for a new payout. | | BVNK:PAYMENT:0007 | Insufficient funds | A wallet does not have sufficient funds to complete the requested transaction. | | BVNK:PAYMENT:0008 | The payment method is not supported | The platform does not currently support the payment method specified in the request. Review the documentation for a list of supported payment methods. | | BVNK:PAYMENT:0009 | Specific functionality is not enabled for this account or wallet | The specific functionality or flow is not enabled or provisioned for this account or wallet. Check your permissions and product enablement. If the feature is available, enable it in the portal; otherwise, contact support to request access. | | BVNK:PAYMENT:0010 | The virtual account has been disabled for this given wallet. Customer should use a different wallet or choose different payment method. | The virtual account assigned to the specified wallet is disabled. Use a different wallet or select an alternative payment method. If you need this virtual account re-enabled, review the wallet configuration and contact support or your account manager. | | **Mass Payouts** | | | | BVNK:PAYMENT:0030 | Could not find a single payment within a given payout batch | The referenced payout batch contains no payments matching your query, or the payments have not been associated with the batch yet. Verify the payout batch identifier, ensure the batch was created successfully, and confirm that items were added and not removed by validation. If you are filtering, check the date/status filters to ensure you are querying the correct environment (sandbox vs. production). | | BVNK:PAYMENT:0031 | Could not find specific payout batch | The payout batch could not be located. The batch identifier may be invalid, belong to another account, or the batch may have been archived/expired. Double-check the batch ID/token and your account permissions, then try again. If the batch was created recently, allow a short delay and retry. | | BVNK:PAYMENT:0032 | Cannot delete a single payout from a batch, as it has started processing already | Once batch processing has started, individual payouts become immutable. Deletions are only permitted while the batch is in a pre‑processing state. To remove items, cancel the whole batch if supported, or create a new batch without the unwanted payout. | | BVNK:PAYMENT:0033 | Cannot update a single payout from a batch, as it has started processing already | Individual payout updates are blocked after batch processing begins. Modify items only before submission. If you need changes, cancel the batch (where supported) and recreate it with corrected payout details, or submit a follow‑up adjustment as a new payout. | ### Fees | **Code** | **Message** | **Description** | | :-----------------: | :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- | | BVNK:FEES:0001 | Invalid wallet selected on client request. | The wallet selected in the client request is invalid. Check that the wallet identifier is correctly formatted and belongs to a valid wallet type. | | BVNK:FEES:0002 | Provided wallet not found for a specific account. | The system was unable to find the specified wallet associated with the requested account. Verify that the wallet exists and is correctly linked to the account. | | BVNK:FEES:0003 | Invalid customer fee wallet configuration. | The customer fee wallet configuration contains invalid settings or parameters. Review and correct the fee wallet configuration in your account settings. | *** ## 1000-1999 | **Code** | **Message** | **Description** | | :---------------: | :------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | | MER-PAY-1000 | Not authorized to perform this action | A user or merchant is not authorised to perform the requested action. This can occur due to insufficient permissions or invalid authentication credentials. | *** ## 2000-2999 :::warning Should you encounter any of the errors outlined in this section, be aware that your payment or transaction attempt will not be successful. The system will reject the transaction at the API level, consequently preventing the creation of a failed transaction record. Therefore, you will not find any evidence of the unsuccessful operation within the Portal. ::: | **Code** | **Message** | **Description** | | :---------------: | :--------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | MER-PAY-2000 | Invalid parameter value | The request parameters fail validation. This includes JSON parsing errors, missing required parameters, invalid data types, or constraint violations on request fields. | | MER-PAY-2001 | Amount x.xx \ failed is less than minimum limit of x.xx \ | The payment amount fails validation. This occurs when the amount is below the minimum limit or above the maximum limit for the specified currency. | | MER-PAY-2002 | Payment has no quote | An error occurs when a payment operation requires an exchange quote, but none is found. This typically occurs when attempting to process payments that require currency conversion. | | MER-PAY-2003 | Exchange quote \ for payment \ has status ACCEPTED so cannot be accepted | An error is given when attempting to modify a quote that has already been finalised. Once a quote is in a final state, it cannot be changed. | | MER-PAY-2004 | Payment has expired | An error occurs when attempting to process a payment that has exceeded its expiration time. Expired payments cannot be processed and need to be recreated. | | MER-PAY-2005 | Instruction validation failed message | The payment instruction data is invalid. This includes validation failures for payment addresses, amounts, or other instruction-specific parameters. | | MER-PAY-2006 | Merchant not found | An error occurs when the Merchant ID provided in the payment request cannot be found in the account. | | MER-PAY-2008 | Payment not found | An error occurs when the payment ID cannot be found. | | MER-PAY-2009 | Invalid request message | Error given for general request validation failures. | | MER-PAY-2010 | A payment with reference \ already exists. Please enter a unique reference. | An error was given when a payment was already created using this reference. All references need to be unique. | | MER-PAY-2011 | Currency \ is disabled | An error occurs when creating a payment with a disabled currency that is not available for trade. | | MER-PAY-2012 | Insufficient funds | A wallet does not have sufficient funds to complete the requested transaction. | | MER-PAY-2014 | Exchange quote \ for payment \ can no longer be accepted as acceptance has expired | A quote is accepted after it has expired. | | MER-PAY-2015 | Crypto instruction not found for payment \ | A required crypto payment instruction cannot be found for the specified payment. | | MER-PAY-2016 | Merchant not authorised to perform this action | A merchant is not authorised to perform the requested action. | | MER-PAY-2017 | Cannot cancel payment with external id \ and status PROCESSING | An attempt is made to cancel a payment and the status is not COMPLETE or PENDING. | | MER-PAY-2018 | Resource modified by another request | A concurrent modification conflict arises. This happens when multiple requests attempt to modify the same resource simultaneously. | | MER-PAY-2019 | Currency \ not found | The specified currency is not found in the system. | | MER-PAY-2024 | protocol \ not found for currency \ | The specified protocol is not found or not supported for the given currency. | | MER-PAY-2025 | Please enter your own wallet address, not the address that you paid into originally. | The provided refund address fails validation checks. | | MER-PAY-2027 | address \
has failed validation for currency: \, protocol: \ and tag: \ | The crypto payout receive address format is invalid. | | MER-PAY-2028 | We couldn't process your payout request to this address: \
this time, please try another address. | The crypto payout receiving address is rejected due to risk and compliance reasons. | | MER-PAY-2029 | Protocol with code \ not found | The specified protocol code is not found in the system. | | MER-PAY-2030 | Unable to find protocol \ for currency code \ | The specified protocol cannot be found for the given currency code. | | MER-PAY-2031 | Account not enabled for flow: \. Please check your permissions or contact support for more information. | The account is not enabled for the specific payment flow being requested. | | MER-PAY-2033 | Multiple protocols found for currency \ Please specify which one to use: \. | Multiple protocols are available for a currency, but no specific protocol is selected, rendering automatic selection impossible. | | MER-PAY-2035 | mass payout data row validation failed | One or more rows in a mass payout file fail validation checks. | | MER-PAY-2036 | \ payments uploaded. This exceeds the maximum number of \ rows. Please reduce your file to \ rows. | A mass payout file contains more rows than the system maximum allows. | | MER-PAY-2037 | Failed to process mass upload file. File name already exists. | Attempting to upload a mass payout file with a filename that already exists. | | MER-PAY-2038 | Failed to process mass upload file. File is required. | A required mass payout file is not provided. | | MER-PAY-2039 | Failed to process mass upload file. One of Merchant id or Wallet id fields have to be provided. | A mass payout request is missing both the required merchant ID and wallet ID parameters. | | MER-PAY-2040 | Failed to process mass upload file. Header is invalid. | The mass payout file contains invalid or missing required headers. | | MER-PAY-2041 | Deleting mass payout item with status CREATED is not allowed. | An error is given when attempting to delete a mass payout item that is not in a deletable state. | | MER-PAY-2042 | Updating mass payout item with status \ is not allowed. | An error is given when attempting to edit a mass payout item that is not in an editable state. | | MER-PAY-2043 | One of merchantId or walletId properties have to be provided, both are missing | A request requires a merchant ID or a wallet ID, but neither is provided. | | MER-PAY-2044 | network \ not found | The specified blockchain network is not valid or supported. | | MER-PAY-2045 | Payout details currency \ and network \ mismatch from previously provided currency \ and network \ | The payout details do not match the details previously provided for the estimate. | | MER-PAY-2046 | protocol not found for currency \ and network \ | The specified protocol and network combination is not valid or supported. | | MER-PAY-2047 | Estimate cannot be modified. Payment with uuid \ already created for estimate \. | An error is given when attempting to accept an estimate that already has an associated payment. | | MER-PAY-2048 | Failed to process mass upload file. File is empty. | The uploaded mass payout file contains no data rows. | | MER-PAY-2049 | Recipient address is not associated with a contact. You will assign or add a contact to it on the next step. | The specified contact cannot be found for the given address or criteria. | | MER-PAY-2050 | Recipient address is associated with more than 1 contact. You'll be asked to choose which contact to use for this transaction. | Multiple matching contacts are found for the provided address. | | MER-PAY-2051 | Crypto account \ is not verified. Please verify your crypto account before using it | An error occurs when attempting to use an unverified cryptocurrency account that requires verification. | | MER-PAY-2052 | This country is unavailable. If you require this region, please reach out to your account manager. | Geographical screening encounters an error or fails due to regional restrictions. | | MER-PAY-2053 | The payment can not be processed. | Geographical screening determines that the payment cannot be processed due to regional restrictions. | | MER-PAY-2054 | Payment direction 'IN' is not compatible with flow 'EMBEDDED_CRYPTO' | An error is given when attempting to use an unsupported payment direction with an embedded crypto flow. | | MER-PAY-2055 | mesh client invalid request message | A request to the mesh client is invalid or malformed. | | MER-PAY-2056 | Customer not found for reference: \ | The specified customer cannot be found in the system. | | MER-PAY-2057 | required header missing | A required HTTP header is missing from the request. | | MER-PAY-2058 | Cannot confirm/accept payment \ as it has been cancelled | An error is given when attempting to perform operations on a payment that has been cancelled. | | MER-PAY-2059 | Wallet with ID \ not found | The specified wallet ID does not exist. | *** ## 3000-3999 ### Network | **Code** | **Message** | **Description** | | :------------: | :------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | BVNK-3001 | Received Bad Request | The system received a request with invalid parameters or missing required information. Check the request format, parameters, and ensure all required fields are properly filled and formatted correctly. | | BVNK-3003 | Forbidden → received request with wrong authentication details | Access to the requested resource is denied due to authentication failure. Ensure your API keys, tokens, or credentials are valid, correctly included in the request header, and have the necessary permissions to perform this operation. | | BVNK-3040 | Transfer not found | The requested transfer could not be located in the system. Verify that the transfer identifier is correct and exists within your account. If you recently initiated the transfer, it may still be processing. | ### Customers | **Code** | **Message** | **Description** | | :--------------- | :---------------------------------------------- | :------------------------------------------------------------------------ | | MER-PAY-3003 | Resource not found | Error given when a requested static resource or endpoint cannot be found. | | MER-PAY-3XXX | Unexpected error - please contact administrator | Error given for unexpected or unhandled exceptions. | *** ## 4001-4999 ### Wallets | **Code** | **Message** | **Description** | | :-------------------: | :----------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | BVNK:LEDGER:4001 | Received Bad Request | The system received a request with invalid parameters or missing required information. Check the request format, ensure all required fields are properly filled, and validate that parameter values meet the expected format and constraints. | | BVNK:LEDGER:4003 | FORBIDDEN! You don't have the necessary permissions to access this resource. | Access to the requested resource is denied due to insufficient permissions. This could be due to missing capabilities when creating a customer wallet, or because the customer has been rejected in the system. Verify your account permissions and customer status before retrying. | | BVNK:LEDGER:4004 | Wallet creation request with the same idempotency key and account reference exists | A wallet creation request with the same idempotency key and account reference already exists in the system. Idempotency keys must be unique for each new wallet creation. Check your records to find the existing wallet or use a new idempotency key for a new wallet. | | BVNK:LEDGER:4005 | Wallet creation request with missing data, one of the customer reference and external reference has to be provided | The wallet creation request is missing essential identifying information. Either a customer reference or an external reference must be provided in the request. Review your request data and include at least one of these required reference identifiers. | ### Customers | **Code** | **Message** | **Description** | | :---------------: | :---------------------------------------------------------------------------- | :------------------------------------------------------------- | | MER-PAY-4011 | Customer fee wallet not found. Please contact support if you need assistance. | The customer fee wallet has not been set. Configuration error. | | MER-PAY-4XXX | Unexpected error - please contact administrator | Error given for unexpected or unhandled exceptions. | ### Rules | Error Code | Message | Description | | ---------------------------: | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | bvnk:payment:rules:4001 | Wallet configuration already exists | A wallet configuration with the specified parameters already exists in the system. This error occurs when attempting to create a duplicate wallet configuration that conflicts with an existing one. | | bvnk:payment:rules:4002 | Address validation error | The provided wallet address failed validation checks. This error indicates that the address format is invalid, doesn't match the expected format for the specified network, or contains invalid characters. | | bvnk:payment:rules:4003 | Invalid currency | The specified currency code is not recognized or supported by the system. Ensure you're using a valid currency identifier that matches the supported currencies for your account. | | bvnk:payment:rules:4004 | Unsupported network | The blockchain network specified in the request is not supported by the payment rules system. Check the list of supported networks and use a valid network identifier. | | bvnk:payment:rules:4005 | Invalid party details | The party details provided in the request contain invalid or missing information. This includes issues with party identification, contact information, or other required party-specific data. | | bvnk:payment:rules:4006 | Data integrity violation | A data integrity constraint has been violated. This error occurs when the provided data conflicts with existing database constraints or business rules that ensure data consistency. | | bvnk:payment:rules:4007 | Invalid fee type | The fee type specified in the request is not valid or supported. Check the documentation for valid fee type values and ensure you're using the correct fee structure for your configuration. | | bvnk:payment:rules:4008 | Invalid destination type | The destination type provided is invalid or not supported for the current operation. Verify that you're using a supported destination type for your wallet configuration. | | bvnk:payment:rules:4016 | Invalid request format | The request format is malformed or contains invalid structure. This error indicates issues with JSON formatting, missing required fields, or incorrect data types in the request payload. | | bvnk:payment:rules:4009 | Wallet not found | The specified wallet could not be found in the system. Verify that the wallet ID or identifier exists and that you have the necessary permissions to access it. | | bvnk:payment:rules:4010 | Wallet configuration not found | The requested wallet configuration does not exist. This error occurs when trying to access, modify, or delete a wallet configuration that is not present in the system. | | bvnk:payment:rules:4011 | Wallet configuration already active | The wallet configuration is already in an active state and cannot be modified or activated again. Check the current status of the configuration before attempting to change it. | | bvnk:payment:rules:4012 | Currency mismatch | There is a mismatch between the expected currency and the provided currency. Ensure that all currency references in your request are consistent and match the wallet's configured currency. | | bvnk:payment:rules:4013 | Wallet status validation error | The wallet status validation failed. This error occurs when the current wallet status doesn't allow the requested operation or when the wallet is in an invalid state for the action being performed. | | bvnk:payment:rules:4014 | Wallet configuration state error | The wallet configuration is in an invalid state for the requested operation. This may occur when trying to perform actions on configurations that are pending, disabled, or in transition states. | | bvnk:payment:rules:4015 | Invalid customer fee configuration | The customer fee configuration contains invalid parameters or values. Check that fee amounts, percentages, and fee structure parameters are within acceptable ranges and properly formatted. | ## 5000-5999 ### Wallets | **Code** | **Message** | **Description** | | :-------------------: | :----------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | BVNK:LEDGER:5001 | Wallet not found | The requested wallet could not be located in the system. Verify that the wallet identifier is correct and exists within your account. | | BVNK:LEDGER:5050 | Wallet balance not found | The system could not retrieve balance information for the specified wallet. Ensure the wallet exists and has been properly initialized with a balance. | | BVNK:LEDGER:5040 | Bad Request | The request contained invalid data, malformed parameters, or violated validation constraints. Review your request format and ensure all required fields contain valid data. | | BVNK:LEDGER:5051 | Wallet creation failed | The wallet creation process failed to complete successfully. This could be due to system constraints, validation issues, or conflicts with existing data. Check the request details and try again with valid parameters. | ### Customers | **Code** | **Message** | **Description** | | :---------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------- | | MER-PAY-5001 | The requested wallet could not be located in the system. | Verify that the wallet identifier is correct and exists within your account. | | MER-PAY-5050 | The system could not retrieve balance information for the specified wallet. | Ensure the wallet exists and has been properly initialized with a balance. | | MER-PAY-5040 | The request contained invalid data, malformed parameters, or violated validation constraints. | Review your request format and ensure all required fields contain valid data. | | MER-PAY-5051 | The wallet creation process failed to complete successfully. This could be due to system constraints, validation issues, or conflicts with existing data. | Check the request details and try again with valid parameters. | ### Rules | **Code** | **Message** | **Description** | | :--------------------------: | :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | bvnk:payment:rules:5001 | Feature not implemented | The requested feature or functionality is not yet implemented in the current version of the payment rules system. This feature may be available in future releases. | | bvnk:payment:rules:5003 | Wallet validation error | Wallet validation services are currently unavailable. This error indicates a temporary issue with external validation services or network connectivity problems affecting wallet verification. | | bvnk:payment:rules:5004 | Internal error | An unexpected internal server error occurred while processing the request. This indicates a system-level issue that should be reported to technical support for investigation. | --- ## Idempotency Some API endpoints support idempotency, allowing you to safely retry requests without duplicating actions. To use this feature, include an `X-Idempotency-Key` header in your request. The key must be a unique, UUID-formatted 36-character alphanumeric value. A request with a new idempotency key will be processed. If you resend the same request with identical credentials and idempotency key, BVNK returns a 400 `Bad Request` status and indicates that a request with this ID already exists. This feature is especially useful for critical operations such as transferring funds, creating payment orders, or updating resources. For example, if a payment order request times out due to a network issue, you can repeat the call with the same idempotency key to ensure only one payment order is created. Only successful requests are cached. Failed requests are not stored, so you can retry them without risk of conflict. ```curl Idempotent Request curl --request POST \ --url https://api.sandbox.bvnk.com/ledger/v1/wallets \ --header 'X-Idempotency-Key: 1e74002e-74a1-48fd-b707-147c3187a3e1' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' ``` :::info For v.2 endpoints, use `Idempotency-Key` header instead of `X-Idempotency-Key`. ```bash curl --request POST \ --url https://api.sandbox.bvnk.com/payment/v2/transfers \ --header 'Idempotency-Key: 1e74002e-74a1-48fd-b707-147c3187a3e1' \ --header 'accept: application/json' \ --header 'content-type: application/json' \ --data ' ``` ::: --- ## Metadata You can include custom metadata in some requests. For example, in the payout requests the metadata will be associated with the payout and included in webhook events related to this payout. To add metadata, include a top-level JSON object with the key `metadata`. The value of this key should be another JSON object containing your desired key-value pairs. ```json Example Request with Metadata { ... "customerId": "ba388054-4512-441e-a9c4-cbe9b0fe0332", "metadata": { "orderId": "PO-2025-001", "internalUserId": "user-456", "reason": "Customer withdrawal" } } ``` When adding metadata to your request, keep the following in mind: * You can include up to 10 separate key-value pairs. * Each metadata key and value must have at least one character. * Each metadata key can be up to 40 characters long. Each value can contain a maximum of 255 characters. * Metadata keys can only contain: * Uppercase letters (A-Z) * Lowercase letters (a-z) * Numbers (0-9) * Underscores (\_) * Hyphens (-) * Metadata values cannot contain the characters "\<>". The provided metadata will also be returned in the status endpoint for reference. --- ## Overview The BVNK RESTful API is designed to enable seamless and secure transactions, including payments, channels, and digital wallet operations. The API operates over HTTP, and all requests and responses are formatted as JSON objects. To gain access to the API, create an account on the BVNK Portal. Once you've completed the signup process and acknowledged our terms, create API keys (Hawk Auth ID and Secret Key). BVNK API has two environments: * Production: [https://api.bvnk.com/](https://api.bvnk.com/) * Sandbox: [https://api.sandbox.bvnk.com/](https://api.sandbox.bvnk.com/) Note that items cannot be transferred between these environments. The sandbox environment is limited to test your integration safely before going live. You can request access to the Production API via the Portal. --- ## Pagination By default, most of the "List" endpoints return paginated results. Use the query parameters `page` and `size` to control the number of results displayed. ```curl Pagination Example curl --request GET \ --url 'https://api.sandbox.bvnk.com/ledger/v1/wallets? page=2&size=30&sort=currencyCode%2Cdesc&offset=1' \ --header 'accept: application/json' ``` | Parameter | Description | | :-------- | :--------------------------------------------------------------------------------------------- | | `page` | Page number. Starts from "0". | | `size` | Number of records on the page returned in the response. Maximum number: 100. Example, `2`. | | `sort` | Sorting criteria. Format: `property,`. Default sort order is ascending. | | `offset` | Starting point for pagination. Use instead of `page`, for example for cursor-based pagination. | In the response, the `pageable` object may be returned. The object provides detailed info on the pagination. ```json { "pageable": { "pageNumber": 0, "pageSize": 20, "sort": { "empty": true, "unsorted": true, "sorted": false }, "offset": 0, "paged": true, "unpaged": false }, "last": true, "totalPages": 1, "totalElements": 2, "first": true, "size": 20, "number": 0, "sort": { "empty": true, "unsorted": true, "sorted": false }, "numberOfElements": 2, "empty": false } ``` | Attribute | Description | | ------------------ | -------------------------------------------------------------------------------------------------------------- | | `pageNumber` | Current page number, for example, `0` for the first page. | | `pageSize` | Number of records per page, for example, `2`. | | `sort.empty` | Indication whether the sort criteria is empty. `true` means no sorting. | | `sort.sorted` | Indication whether the results are sorted. `false` means no sorting. | | `sort.unsorted` | Indication whether the results are unsorted. `true` means no sorting. | | `offset` | Offset of the first record, for example, `0`, which means no offset is applied. | | `paged` | Indication whether the results are paginated. `true` for paginated. | | `unpaged` | Indication whether the results are not paginated (false). | | `last` | Indication whether this is the last page of results. `false` means there are more pages after the current one. | | `totalPages` | Total number of pages, for example, `10`. | | `totalElements` | Total number of elements across all pages, foe example, `20` elements. | | `first` | Indication whether this is the first page. `true` for yes. | | `size` | Number of elements per page, for example, `2`. | | `number` | Current page number, foe example, `0` for the first page. | | `sort.empty` | Indication whether the sort criteria is empty. `true` means no criteria. | | `sort.sorted` | Indication whether the results are sorted. (false). | | `sort.unsorted` | Indication whether the results are unsorted. `true` for unsorted. | | `numberOfElements` | Number of elements on the current page, for example, `2` items. | | `empty` | Indication whether the page is empty. `false` means the page contains elements. | --- ## Postman Collection You can easily test our API before you start developing your app using our Postman Collection.\ This usually helps out immensely prior to any development process has even started. [Download the Postman app](https://www.postman.com/downloads/) and fork the collection directly by clicking the button below: ## How to setup the collection 1. Choose Environments -> BVNK Public Environment 2. Enter the relevant HAWK AUTH ID and HAWK AUTH KEY that were displayed when you created your API keys ![](https://files.readme.io/3587036-Environment.png "Environment.png") ## Pick an API action from the list To test individual endpoints and send requests, explore them on the left-hand side of your Postman. ![](https://files.readme.io/23e5af9-API.png "API.png") --- ## BVNK API Endpoints The BVNK API is designed to facilitate seamless and secure transactions including payments, channels, and digital wallet transactions. Hawk Payload (see: https://github.com/hueniverse/hawk) Security Scheme Type: apiKey Header parameter name: Authorization Bearer token for authentication. Security Scheme Type: http HTTP Authorization Scheme: bearer Bearer format: JWT --- ## Create Channel Creates a channel that your end users can openly send payments to. --- ## List Channels Retrieves all channels related to a Wallet ID. --- ## List Channel Payments Retrieves a list of payments to a channel on a specific Wallet ID. --- ## Get Channel Payment Retrieves a specific payment made into a channel. --- ## Get Channel Retrieves a specific channel by its UUID. --- ## Get Channel Spot Rate Poll the current spot rate for a channel. Use this endpoint if you are building your own UI and need to display the live conversion rate to customers. --- ## Complete Onboarding :::caution deprecated The endpoint is no longer used in the onboarding flow. Now, onboarding procedures are completed automatically once you provide all the required documents and meet the Customer compliance requirements. ::: Sends the confirmation of the onboarding completion. * In the sandbox environment, Customer verification is automatically approved. * In production, if the EPC meets all compliance requirements, the status will update to `VERIFIED`. If there are outstanding requirements or issues, the `INFO_REQUIRED` status is assigned, prompting manual review or further actions. If the verification is unsuccessful, the `REJECTED` status is assigned. **Note**: Run this endpoint 10 seconds after uploading the documents. This time is needed for the system to acknowledge the documents and update the status. --- ## Create or Update Account Webhook URL :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: Creates or updates the the main account webhook URL that all webhooks are sent to. --- ## Create Agreement Signing Session Create Agreement Signing Session --- ## Create Wallet Creates a fiat or crypto wallet for a specific Embedded Partner Customer, generating a unique virtual account tied to the wallet. --- For more information, how to apply it in the Embedded Wallets flow, refer to the [Create a customer wallet](https://docs.bvnk.com/docs/step-2-creating-a-customer-wallet-and-virtual-account#/create-a-customer-wallet) guide. --- ## Create Customer Creates an Embedded Partner Customer account. The customer can be 'Individual' or 'Company'. --- ## Onboard Embedded Merchant :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: Creates the Embedded Partner Merchant account. --- ## Create Report Schedule Creates a new scheduled report that will be automatically generated and delivered according to the specified frequency and delivery preferences. Each user can create only one schedule per account. --- ## Remove a Document Deletes a specified document from a Customer's profile. --- ## Delete Report Schedule Deletes an existing report schedule. Only the user who created the schedule can delete it. Once deleted, no further reports will be generated according to this schedule. --- ## Estimate Refund Fee Returns the estimated processing fee and the maximum remaining refundable balance for the pay-in. The fee will be deducted from your wallet when issuing a refund. The fee amount may vary depending on the merchant contract with BVNK. --- ## Fetch Account Webhook URL :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: Fetches the the main account webhook URL that all webhooks are sent to. --- ## Retrieve Agreement Session Status Returns the current status of an agreement session using its reference ID. --- ## Fetch Agreements Fetch all required agreement documents needed to onboard an Embedded Partner Merchant. --- ## Get Customer Documents Retrieves the details of the specific document added for the Customer. If no parameters are specified, retrieves the array of available documents. --- ## Get Customer Fee Wallets Retrieves the list of available wallets that can receive customer fees. For more information, refer to [Customer fees](https://docs.bvnk.com/docs/charge-customer-fees#/). --- ## List Required Information Retrieves detailed information about what documents and data are required for customer onboarding when their status is `INFO_REQUIRED`. This endpoint provides specific requirements for documents, questionnaires, and additional data needed to complete the onboarding process. --- ## Get Customer Get Customer --- ## Get Document Download URL Retrieves a temporary URL for document download. --- ## List Industries Retrieves a definitive list of industries and sub-industries, which can be included in the payload when creating a customer. --- ## List Monthly Expected Volumes Retrieves a definitive list of Expected Monthly Volume values, which can be included in the payload when creating a customer. --- ## Get Questionnaire Definition Fetches quiestionnaire schemas with questions, answer types, option lists. You can render the questionnaire in your UI. Questionnaires include the following sections:**Nature of Business**: mandatory details related to company operations)**Document Requirements**: key compliance documents --- ## Search Questionnaire Submissions Returns previously submitted questionnaire answer sets matching the supplied filters --- ## Get Supported Timezones Retrieves a list of all supported timezones that can be used when creating or updating report schedules. Each timezone includes both the IANA timezone identifier and a human-readable label with UTC offset. --- ## List all Customer Wallets Retrieves a list of wallets associated with Embedded Partner Customer. Request without parameters returns all existing wallets. Specify `customerReference` in query to retrieve wallet details of the specific EPC. `. Default sort order is asc.","required":false,"schema":{"type":"string","example":"currencyCode,desc"}},{"name":"offset","in":"query","description":"Starting point for pagination, used instead of `page. Useful for cursor-based pagination.","schema":{"type":"integer","minimum":0,"default":0}}]} > --- ## Get Wallet Retrieves a specific wallet by its ID --- ## List Customers Retrieves the full list of Customers. --- ## Fetch Country Codes Fetch list of all countries and associated ISO codes. --- ## List Crypto Currencies Retrieves a list of all cryptocurrencies available on the BVNK platform. This list represents cryptocurrencies that end users can select whilst making a payment. For sandbox, only Ethereum (ETH) is fully functional. --- ## List Wallet Currencies These are the currencies that can be used to create a new wallet. --- ## List Fiat Currencies Retrieves a list of all display fiat currencies available on BVNK's Crypto Payments API. This list refers to currencies merchants can display on a payment page to an end user. It does not represent the list of currencies that can be held on the platform in wallets. --- ## List Exchange Rates Lists available exchange rates for a given currency. --- ## List Report Schedules Retrieves a paginated list of report schedules for the authenticated user's account. Returns all schedules created by the user with their current configuration and delivery preferences. --- ## Create Merchant ID Generate a Merchant ID for your account to process pay-ins and pay-outs through our API. A Merchant ID is essential as it designates the account wallet where incoming pay-ins will be settled. For instance, if a Merchant ID is associated with a EUR wallet ID, any incoming USDT payment will be automatically converted to EUR and deposited in the designated EUR wallet. Vice versa, any outgoing USDT payment will be automatically converted and withdrawn from the designated EUR wallet. For further information, please visit https://docs.bvnk.com/docs/creating-your-first-merchant to learn more about creating your first Merchant ID. --- ## List Merchant IDs Retrieves merchant IDs setup on your account. --- ## Accept an Estimated Payout Accepts the current estimate and converts it into a pending crypto payment. --- ## Get details of an estimate payout by ID Retrieves the current estimate state by ID. --- ## Update an Existing Estimate Payout Refreshes the quote using the latest exchange rates and fees. Call periodically (e.g., every 30 seconds) with either `walletRequiredAmount` or `paidRequiredAmount` to recalculate the other. --- ## Create an Estimate Payout Retrieves latest exchange rates, fees and network costs for a crypto payout without creating an actual payout. Provide either `walletRequiredAmount` (amount to send) or `paidRequiredAmount` (amount the recipient should receive). See the [Estimate Crypto Payouts](https://docs.bvnk.com/docs/estimate-crypto-payouts#/) guide for details. --- ## Accept Payment Accepts a pending payment with currency or payout information. --- ## Confirm Payment Confirms a two-step payout. --- ## Create Payment Creates an incoming (type IN) or outcoming (type OUT) crypto payment. Alternatively, it creates a crypto payment that is either sent via a Customer's wallet or deposited into it. For more information, see the [Make Crypto Payments](https://docs.bvnk.com/docs/create-a-crypto-payout#/) guide. There are two flows depending on specifying `payOutDetails`: - If you add `PayOutDetails` with all its child parameters, the payment is successfully created and the funds are sent to the specified wallet, with no further actions needed. - If not included in the request, in the response you receive `redirectUrl`. This is a link to a page, where to you can redirect your Customer so they can finalize the payment. --- ## List Payments Retrieves a list of payments on a specific Wallet ID. --- ## Validate Address Validates that a crypto address is correct. Use this endpoint to validate that an address exists, is correctly formatted, and includes all the required data. This endpoint can help prevent your end users losing funds when submitting a payout. --- ## Get Payment Retrieves details of a specific payment using the UUID of the payment. --- ## Create on-ramp payment rule Creates a rule that links a crypto wallet to a fiat virtual account to automatically handle on-ramp (fiat → crypto) flow. The `trigger` field defines the flow to activate: - `payment:payin:fiat` — on-ramp For more information, see the [Automate Fiat-to-Crypto Transfers](https://docs.bvnk.com/docs/automate-fiat-to-crypto-transfers#/) guide. --- ## Update Payment Rule Partially updates a previously created payment rule. Only properties included in the request are updated; omitted fields remain unchanged. Setting a nullable property to null clears it. --- ## Update Payment Updates a pending payment with currency or payout information. --- ## Confirm Beneficiary's Name Confirms a payout when the beneficiary name check returns a mismatch warning. If not confirmed within five minutes, the action expires. In this case, make a new payout. --- ## Initiate Payout (ver. 2) Creates a payout to business or individual customers. In case of payment in Euro, the transaction is subject to **Verification of Payee** (VoP): if the beneficiary's name in the request doesn't match the name in the beneficiary's bank account, the status 202 Verification Failed is returned. To proceed with the payment, you must [Confirm Beneficiary's name](https://docs.bvnk.com/reference/payoutconfirmbeneficiaryv2#/). For the details, refer to the [Initiate Payouts (ver. 2)](https://docs.bvnk.com/docs/initiate-payment-2#/) guide. --- ## Retrieve Payout (ver. 2). Retrieves a specific payout. You can also use this endpoint to check the status of the crypto or fiat payout. --- ## Get Payout :::caution deprecated This endpoint is deprecated. Please use the `GET /payment/v2/payouts/:transactionId` endpoint instead. ::: Retrieves a specific payout. You can also use this endpoint to check the status of the crypto or fiat payout. **Note**: The PENDING_APPROVAL status is only relevant for **fiat payouts**. --- ## Accept Quote Executes a quote. --- ## Create Quote Creates a quote to convert currency between wallets. For wallet-to-wallet payment quotes, the payment processing is synchronous. You can expect the following statuses in the response: | When | `quoteStatus` | `paymentStatus` | |------|-------------|---------------| | **Successful payment**|| | | Getting an estimate | `ESTIMATE` | `PENDING` | | Creating a quote | `PENDING` | `PENDING` | | Accepting the quote | `PAYMENT_OUT_PROCESSED` | `SUCCESS` | |**Failed payment**|| | | Pay-in fails | `PAYMENT_IN_FAILED` | `FAILED` | | Conversion fails | `CONVERSION_FAILED` | `FAILED` | | Pay-out fails | `PAYMENT_OUT_FAILED` | `FAILED` | If you don't receive `"paymentStatus": "SUCCESS"` in the response, you can call `/api/v1/quote/{uuid}` to check the conversion progress. --- ## List Quotes Retrieves all quotes on a specific Merchant ID. --- ## Get Quote Retrieves a specific quote. --- ## Get Exchange Rate Provides a mid market exchange rate between two assets. --- ## Refund Pay-in Initiates seamless one-click refund processing for any previous pay-in. Once accepted, a refund transaction appears in your portal and can be acquired via API. Upon activation, the system automatically generates a new transaction according to our standard workflow. You can request multiple refunds for the same transaction, as long as the total refunded amount doesn't exceed what was originally paid in. --- ## Set Wallet for Customer Fees Sets up the customer fee wallet for a specific currency. The customer fee wallet is where the collected customer fees will be credited when processing transactions. For more information, refer to [Customer fees](https://docs.bvnk.com/docs/charge-customer-fees#/). --- ## Simulate Pay-in (ver. 2) Simulates pay-ins with different currencies and payment methods in the sandbox environment. This version allows specifying originator entity details (individual or company) for more comprehensive testing scenarios. --- ## Simulate Pay-in :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: Simulates pay-ins with different currencies and payment methods in the sandbox environment. --- ## Submit Questionnaire Submit questionnaire answers provided by EPCs. BVNK validates completeness asynchronously and transitions the customer to the next onboarding state when all required answers are present. In case some answers are missing, BVNK specifies them and waits for all the answers to be submitted --- ## Create Internal Transfer (ver. 2) Creates an internal stablecoin and fiat transfer between EPC and EP wallets. Payment combinations: - Crypto to crypto - Fiat to fiat --- ## Create Internal Transfer :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: Create an internal Fiat transfer to an existing beneficiary wallet. --- ## Get Transfer (ver. 2) Retrieves a specific transfer details. --- ## Get Transfer :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: Retrieves a specific transfer details. --- ## List Transfer Beneficiaries Retrieves a list of beneficiaries eligible for transfers for a specified wallet. --- ## Update Agreement Session Status Updates the signing status of an agreement session. Typically used after user submission to mark the session as SIGNED or DECLINED. --- ## Update Existing Report Schedule Updates an existing scheduled report with new frequency and delivery preferences. Only the user who created the schedule can update it. --- ## Upload Documents to Customer Attach one or more documents to a customer's profile. This can be either a company customer (with optional linkage to a specific associate) or an individual customer. Files must be base64-encoded before submission. - For company-level documents, omit `customerPersonReference`. - For associate-level documents, provide the `customerPersonReference`. - The customer must be in `INFO_REQUIRED` status. --- ## Verify Beneficiary's Name Verifies the beneficiary's name. You can call this endpoint before creating a payout via [`POST /payment/v2/payouts`](https://docs.bvnk.com/reference/payoutcreatev2#/) to verify the beneficiary's name and receive a verification ID that can be used in subsequent payout requests. When using this endpoint, make sure you specify the same beneficiary's names as you will use in the payload of the [Initiate Payout](https://docs.bvnk.com/reference/payoutcreatev2#/) request. --- ## List Wallet Balances Retrieves the balances of your wallets on platform. --- ## Create Wallet(Bvnk-main-api) Creates a wallet on the BVNK platform. --- ## List Transactions Retrieves a paginated list of transactions for a specific wallet. Supports filtering by `walletId` and optional date range. The date range can be applied to the `createdAt` or `updatedAt` timestamp, determined by the `filterMode`. If omitted, the `filterMode` defaults to `CREATED_AT`. Refer to the [Fiat payments guide](https://docs.bvnk.com/docs/listing-transactions). --- ## List Wallets Retrieves a list of wallets on your account. Displays the first 10 wallets without max set to higher --- ## List Wallet Profiles Returns available wallet profiles based on optional filters for currency codes and payment methods. --- For more information, how to apply it in the Embedded Wallets flow, refer to the [Assign a wallet profile](https://docs.bvnk.com/docs/step-2-creating-a-customer-wallet-and-virtual-account#/assign-a-wallet-profile) guide. --- ## Get Wallet(Bvnk-main-api) Retrieves detailed information about a specific wallet. --- ## Transactions Report V2 :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: Report all transactions from wallet in specified format. Report will be generated and sent to main account email in the specified format. --- ## Transactions Report Creates a report of all transactions from a wallet in the specified format and sends it via the preferred delivery method: WEBHOOK or EMAIL. **Note**: Reports are delivered instantly but the data has a one-hour lag for Production environment and eight-hour delay in sandbox. For example, a report requested at 9:00 AM will include transactions only up to 8:00 AM. See the [Receive Transactions Report via Webhook](https://docs.bvnk.com/docs/receive-transaction-history-report-via-webhook) guide for more information. --- ## BVNK Webhooks API BVNK Webhooks API provides real-time notifications for various events across the BVNK platform. This API specification describes the webhook events that BVNK sends to your configured endpoints. ## Webhook Categories - **Status Change Webhooks**: Fiat and crypto payment status changes - **Channel Webhooks**: Crypto channel transaction events - **Crypto Webhooks**: Cryptocurrency transaction lifecycle events - **Customer Webhooks**: Customer management and compliance events - **Ledger Webhooks**: Wallet creation and reporting events - **Digital Asset Webhooks**: Deposit and withdrawal status changes ## Authentication All webhooks include an `x-signature` header for verification using HMAC-SHA256. The signature is calculated using your webhook secret key and the raw JSON payload. ## Response Requirements Your webhook endpoint must respond with HTTP 200 status code to acknowledge receipt. The retry policy adds a delay before each retry, starting with a base delay and increasing the time between retries. 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. ## 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. HMAC-SHA256 signature for webhook verification. The signature is calculated using your webhook secret key and the raw JSON payload. Example verification in different languages: **JavaScript:** ```javascript const crypto = require('crypto'); const signature = crypto .createHmac('sha256', secretKey) .update(payload, 'utf8') .digest('base64'); ``` **Python:** ```python signature = base64.b64encode( hmac.new(secretKey.encode('utf-8'), payload.encode('utf-8'), hashlib.sha256).digest() ).decode('ascii') ``` Security Scheme Type: apiKey Header parameter name: x-signature --- ## Channel transaction confirmed The `bvnk:payment:channel:transaction-confirmed` webhook is triggered when a new channel transaction is confirmed on chain, and the transaction has been processed. Request --- ## Channel transaction detected The `bvnk:payment:channel:transaction-detected` webhook is triggered when a new channel transaction is detected, and the transaction has not yet been confirmed. Request --- ## Channel transaction placed on hold The `bvnk:payment:channel:transaction-on-hold` webhook is triggered when a transaction sent to a channel is placed on hold under BVNKs compliance programme. If the payment is released, you will receive the `bvnk:payment:channel:transaction-confirmed` event. Request --- ## Cryptocurrency refund initiated Triggered when a cryptocurrency payment refund is initiated. This webhook indicates that a refund process has been started for a crypto payment. Request --- ## Cryptocurrency payment status change Triggered when the status of a cryptocurrency payment changes. This webhook provides real-time updates on crypto payment processing status. Request --- ## Cryptocurrency transaction late Triggered when a cryptocurrency transaction is received after the payment has expired or been completed. This webhook indicates that funds were received for a payment that is no longer active. Request --- ## Cryptocurrency transaction on hold Triggered when a cryptocurrency transaction is placed on hold under BVNK's compliance program. This webhook indicates that the transaction requires manual review before processing. Request --- ## Cryptocurrency transaction settled Triggered when a cryptocurrency transaction is settled. This webhook indicates that the transaction has been fully processed and settled. Request --- ## Customer document status change notification Triggered when a customer document verification status changes. This webhook provides real-time updates on document compliance status. Request --- ## Customer status change notification Triggered when a customer's status changes to `INFO_REQUIRED`, `PENDING`, `VERIFIED`, or `REJECTED`. This webhook provides real-time updates on customer onboarding and compliance status. Request --- ## Customer update notification Triggered when customer information is updated. This is usually done by a verification provider's link when customer data is populated from the verification provider. Request --- ## Ledger report ready notification Triggered when a ledger report is ready for download. This webhook provides real-time updates on report generation completion. Request --- ## Ledger wallet creation notification A wallet creation webhook provides real-time notifications to your system when a new wallet is successfully created. Request --- ## Fiat pay-in status change notification (v2) Triggered when the status of an incoming fiat payment changes (version 2). This webhook provides real-time updates on pay-in processing status with enhanced data structure. Request --- ## Fiat payment status change notification Triggered when the status of an incoming fiat payment changes. This webhook provides real-time updates on payment processing status. Request --- ## Fiat payout status change notification (v2) Triggered when the status of an outgoing fiat payment changes (version 2). This webhook provides real-time updates on payout processing status with enhanced data structure. Request --- ## Fiat payout status change notification (v1) Triggered when the status of an outgoing fiat payment changes (version 1). This webhook provides real-time updates on payout processing status. Request --- ## Internal transfer status change notification (v2) Triggered when the status of an internal payment transfer changes (version 2). This webhook provides real-time updates on internal transfer processing status with enhanced data structure. Request --- ## Internal transfer status change notification :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: Triggered when the status of an internal payment transfer changes. This webhook provides real-time updates on internal transfer processing status. Request --- ## BVNK API Endpoints(Endpoints) The BVNK API is designed to facilitate seamless and secure transactions including payments, channels, and digital wallet transactions. Hawk Payload (see: https://github.com/hueniverse/hawk) Security Scheme Type: apiKey Header parameter name: Authorization Bearer token for authentication. Security Scheme Type: http HTTP Authorization Scheme: bearer Bearer format: JWT --- ## Create Channel(Endpoints) Creates a channel that your end users can openly send payments to. Request --- ## List Channels(Endpoints) Retrieves all channels related to a Wallet ID. Request --- ## List Channel Payments(Endpoints) Retrieves a list of payments to a channel on a specific Wallet ID. Request --- ## Get Channel Payment(Endpoints) Retrieves a specific payment made into a channel. Request --- ## Get Channel(Endpoints) Retrieves a specific channel by its UUID. Request --- ## Get Channel Spot Rate(Endpoints) Poll the current spot rate for a channel. Use this endpoint if you are building your own UI and need to display the live conversion rate to customers. Request --- ## Create Agreement Signing Session(Endpoints) Create Agreement Signing Session Request --- ## Create Wallet(Endpoints) Creates a fiat or crypto wallet for a specific Embedded Partner Customer, generating a unique virtual account tied to the wallet. --- For more information, how to apply it in the Embedded Wallets flow, refer to the [Create a customer wallet](https://docs.bvnk.com/docs/step-2-creating-a-customer-wallet-and-virtual-account#/create-a-customer-wallet) guide. Request --- ## Create Customer(Endpoints) Creates an Embedded Partner Customer account. The customer can be 'Individual' or 'Company'. Request --- ## Create Report Schedule(Endpoints) Creates a new scheduled report that will be automatically generated and delivered according to the specified frequency and delivery preferences. Each user can create only one schedule per account. Request --- ## Remove a Document(Endpoints) Deletes a specified document from a Customer's profile. Request --- ## Delete Report Schedule(Endpoints) Deletes an existing report schedule. Only the user who created the schedule can delete it. Once deleted, no further reports will be generated according to this schedule. Request --- ## Estimate Refund Fee(Endpoints) Returns the estimated processing fee and the maximum remaining refundable balance for the pay-in. The fee will be deducted from your wallet when issuing a refund. The fee amount may vary depending on the merchant contract with BVNK. Request --- ## Retrieve Agreement Session Status(Endpoints) Returns the current status of an agreement session using its reference ID. Request --- ## Fetch Agreements(Endpoints) Fetch all required agreement documents needed to onboard an Embedded Partner Merchant. Request --- ## Get Customer Documents(Endpoints) Retrieves the details of the specific document added for the Customer. If no parameters are specified, retrieves the array of available documents. Request --- ## Get Customer Fee Wallets(Endpoints) Retrieves the list of available wallets that can receive customer fees. For more information, refer to [Customer fees](https://docs.bvnk.com/docs/charge-customer-fees#/). --- ## List Required Information(Endpoints) Retrieves detailed information about what documents and data are required for customer onboarding when their status is `INFO_REQUIRED`. This endpoint provides specific requirements for documents, questionnaires, and additional data needed to complete the onboarding process. Request --- ## Get Customer(Endpoints) Get Customer Request --- ## Get Document Download URL(Endpoints) Retrieves a temporary URL for document download. Request --- ## List Industries(Endpoints) Retrieves a definitive list of industries and sub-industries, which can be included in the payload when creating a customer. --- ## List Monthly Expected Volumes(Endpoints) Retrieves a definitive list of Expected Monthly Volume values, which can be included in the payload when creating a customer. --- ## Get Questionnaire Definition(Endpoints) Fetches quiestionnaire schemas with questions, answer types, option lists. You can render the questionnaire in your UI. Questionnaires include the following sections:**Nature of Business**: mandatory details related to company operations)**Document Requirements**: key compliance documents Request --- ## Search Questionnaire Submissions(Endpoints) Returns previously submitted questionnaire answer sets matching the supplied filters Request --- ## Get Supported Timezones(Endpoints) Retrieves a list of all supported timezones that can be used when creating or updating report schedules. Each timezone includes both the IANA timezone identifier and a human-readable label with UTC offset. --- ## List all Customer Wallets(Endpoints) Retrieves a list of wallets associated with Embedded Partner Customer. Request without parameters returns all existing wallets. Specify `customerReference` in query to retrieve wallet details of the specific EPC. Request `. Default sort order is asc.","required":false,"schema":{"type":"string","example":"currencyCode,desc"}},{"name":"offset","in":"query","description":"Starting point for pagination, used instead of `page. Useful for cursor-based pagination.","schema":{"type":"integer","minimum":0,"default":0}}]} > --- ## Get Wallet(Endpoints) Retrieves a specific wallet by its ID Request --- ## List Customers(Endpoints) Retrieves the full list of Customers. Request --- ## Fetch Country Codes(Endpoints) Fetch list of all countries and associated ISO codes. Request --- ## List Crypto Currencies(Endpoints) Retrieves a list of all cryptocurrencies available on the BVNK platform. This list represents cryptocurrencies that end users can select whilst making a payment. For sandbox, only Ethereum (ETH) is fully functional. Request --- ## List Wallet Currencies(Endpoints) These are the currencies that can be used to create a new wallet. Request --- ## List Fiat Currencies(Endpoints) Retrieves a list of all display fiat currencies available on BVNK's Crypto Payments API. This list refers to currencies merchants can display on a payment page to an end user. It does not represent the list of currencies that can be held on the platform in wallets. Request --- ## List Exchange Rates(Endpoints) Lists available exchange rates for a given currency. Request --- ## List Report Schedules(Endpoints) Retrieves a paginated list of report schedules for the authenticated user's account. Returns all schedules created by the user with their current configuration and delivery preferences. Request --- ## Create Merchant ID(Endpoints) Generate a Merchant ID for your account to process pay-ins and pay-outs through our API. A Merchant ID is essential as it designates the account wallet where incoming pay-ins will be settled. For instance, if a Merchant ID is associated with a EUR wallet ID, any incoming USDT payment will be automatically converted to EUR and deposited in the designated EUR wallet. Vice versa, any outgoing USDT payment will be automatically converted and withdrawn from the designated EUR wallet. For further information, please visit https://docs.bvnk.com/docs/creating-your-first-merchant to learn more about creating your first Merchant ID. Request --- ## List Merchant IDs(Endpoints) Retrieves merchant IDs setup on your account. --- ## Accept an Estimated Payout(Endpoints) Accepts the current estimate and converts it into a pending crypto payment. Request --- ## Update an Existing Estimate Payout(Endpoints) Refreshes the quote using the latest exchange rates and fees. Call periodically (e.g., every 30 seconds) with either `walletRequiredAmount` or `paidRequiredAmount` to recalculate the other. Request --- ## Create an Estimate Payout(Endpoints) Retrieves latest exchange rates, fees and network costs for a crypto payout without creating an actual payout. Provide either `walletRequiredAmount` (amount to send) or `paidRequiredAmount` (amount the recipient should receive). See the [Estimate Crypto Payouts](https://docs.bvnk.com/docs/estimate-crypto-payouts#/) guide for details. Request --- ## Accept Payment(Endpoints) Accepts a pending payment with currency or payout information. Request --- ## Confirm Payment(Endpoints) Confirms a two-step payout. Request --- ## Create Payment(Endpoints) Creates an incoming (type IN) or outcoming (type OUT) crypto payment. Alternatively, it creates a crypto payment that is either sent via a Customer's wallet or deposited into it. For more information, see the [Make Crypto Payments](https://docs.bvnk.com/docs/create-a-crypto-payout#/) guide. There are two flows depending on specifying `payOutDetails`: - If you add `PayOutDetails` with all its child parameters, the payment is successfully created and the funds are sent to the specified wallet, with no further actions needed. - If not included in the request, in the response you receive `redirectUrl`. This is a link to a page, where to you can redirect your Customer so they can finalize the payment. Request --- ## List Payments(Endpoints) Retrieves a list of payments on a specific Wallet ID. Request --- ## Validate Address(Endpoints) Validates that a crypto address is correct. Use this endpoint to validate that an address exists, is correctly formatted, and includes all the required data. This endpoint can help prevent your end users losing funds when submitting a payout. Request --- ## Get Payment(Endpoints) Retrieves details of a specific payment using the UUID of the payment. Request --- ## Create on-ramp payment rule(Endpoints) Creates a rule that links a crypto wallet to a fiat virtual account to automatically handle on-ramp (fiat → crypto) flow. The `trigger` field defines the flow to activate: - `payment:payin:fiat` — on-ramp For more information, see the [Automate Fiat-to-Crypto Transfers](https://docs.bvnk.com/docs/automate-fiat-to-crypto-transfers#/) guide. Request --- ## Update Payment Rule(Endpoints) Partially updates a previously created payment rule. Only properties included in the request are updated; omitted fields remain unchanged. Setting a nullable property to null clears it. Request --- ## Update Payment(Endpoints) Updates a pending payment with currency or payout information. Request --- ## Confirm Beneficiary's Name(Endpoints) Confirms a payout when the beneficiary name check returns a mismatch warning. If not confirmed within five minutes, the action expires. In this case, make a new payout. Request --- ## Initiate Payout (ver. 2)(Endpoints) Creates a payout to business or individual customers. In case of payment in Euro, the transaction is subject to **Verification of Payee** (VoP): if the beneficiary's name in the request doesn't match the name in the beneficiary's bank account, the status 202 Verification Failed is returned. To proceed with the payment, you must [Confirm Beneficiary's name](https://docs.bvnk.com/reference/payoutconfirmbeneficiaryv2#/). For the details, refer to the [Initiate Payouts (ver. 2)](https://docs.bvnk.com/docs/initiate-payment-2#/) guide. Request --- ## Retrieve Payout (ver. 2).(Endpoints) Retrieves a specific payout. You can also use this endpoint to check the status of the crypto or fiat payout. Request --- ## Get Payout(Endpoints) :::caution deprecated This endpoint is deprecated. Please use the `GET /payment/v2/payouts/:transactionId` endpoint instead. ::: Retrieves a specific payout. You can also use this endpoint to check the status of the crypto or fiat payout. **Note**: The PENDING_APPROVAL status is only relevant for **fiat payouts**. Request --- ## Accept Quote(Endpoints) Executes a quote. Request --- ## Create Quote(Endpoints) Creates a quote to convert currency between wallets. For wallet-to-wallet payment quotes, the payment processing is synchronous. You can expect the following statuses in the response: | When | `quoteStatus` | `paymentStatus` | |------|-------------|---------------| | **Successful payment**|| | | Getting an estimate | `ESTIMATE` | `PENDING` | | Creating a quote | `PENDING` | `PENDING` | | Accepting the quote | `PAYMENT_OUT_PROCESSED` | `SUCCESS` | |**Failed payment**|| | | Pay-in fails | `PAYMENT_IN_FAILED` | `FAILED` | | Conversion fails | `CONVERSION_FAILED` | `FAILED` | | Pay-out fails | `PAYMENT_OUT_FAILED` | `FAILED` | If you don't receive `"paymentStatus": "SUCCESS"` in the response, you can call `/api/v1/quote/{uuid}` to check the conversion progress. Request --- ## List Quotes(Endpoints) Retrieves all quotes on a specific Merchant ID. Request --- ## Get Quote(Endpoints) Retrieves a specific quote. Request --- ## Get Exchange Rate(Endpoints) Provides a mid market exchange rate between two assets. Request --- ## Refund Pay-in(Endpoints) Initiates seamless one-click refund processing for any previous pay-in. Once accepted, a refund transaction appears in your portal and can be acquired via API. Upon activation, the system automatically generates a new transaction according to our standard workflow. You can request multiple refunds for the same transaction, as long as the total refunded amount doesn't exceed what was originally paid in. Request --- ## Set Wallet for Customer Fees(Endpoints) Sets up the customer fee wallet for a specific currency. The customer fee wallet is where the collected customer fees will be credited when processing transactions. For more information, refer to [Customer fees](https://docs.bvnk.com/docs/charge-customer-fees#/). Request --- ## Simulate Pay-in (ver. 2)(Endpoints) Simulates pay-ins with different currencies and payment methods in the sandbox environment. This version allows specifying originator entity details (individual or company) for more comprehensive testing scenarios. Request --- ## Simulate Pay-in(Endpoints) :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: Simulates pay-ins with different currencies and payment methods in the sandbox environment. Request --- ## Submit Questionnaire(Endpoints) Submit questionnaire answers provided by EPCs. BVNK validates completeness asynchronously and transitions the customer to the next onboarding state when all required answers are present. In case some answers are missing, BVNK specifies them and waits for all the answers to be submitted Request --- ## Create Internal Transfer (ver. 2)(Endpoints) Creates an internal stablecoin and fiat transfer between EPC and EP wallets. Payment combinations: - Crypto to crypto - Fiat to fiat Request --- ## Create Internal Transfer(Endpoints) :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: Create an internal Fiat transfer to an existing beneficiary wallet. Request --- ## Get Transfer (ver. 2)(Endpoints) Retrieves a specific transfer details. Request --- ## Get Transfer(Endpoints) :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: Retrieves a specific transfer details. Request --- ## List Transfer Beneficiaries(Endpoints) Retrieves a list of beneficiaries eligible for transfers for a specified wallet. Request --- ## Update Agreement Session Status(Endpoints) Updates the signing status of an agreement session. Typically used after user submission to mark the session as SIGNED or DECLINED. Request --- ## Update Existing Report Schedule(Endpoints) Updates an existing scheduled report with new frequency and delivery preferences. Only the user who created the schedule can update it. Request --- ## Upload Documents to Customer(Endpoints) Attach one or more documents to a customer's profile. This can be either a company customer (with optional linkage to a specific associate) or an individual customer. Files must be base64-encoded before submission. - For company-level documents, omit `customerPersonReference`. - For associate-level documents, provide the `customerPersonReference`. - The customer must be in `INFO_REQUIRED` status. Request --- ## Verify Beneficiary's Name(Endpoints) Verifies the beneficiary's name. You can call this endpoint before creating a payout via [`POST /payment/v2/payouts`](https://docs.bvnk.com/reference/payoutcreatev2#/) to verify the beneficiary's name and receive a verification ID that can be used in subsequent payout requests. When using this endpoint, make sure you specify the same beneficiary's names as you will use in the payload of the [Initiate Payout](https://docs.bvnk.com/reference/payoutcreatev2#/) request. Request --- ## List Wallet Balances(Endpoints) Retrieves the balances of your wallets on platform. Request --- ## Create Wallet(3) Creates a wallet on the BVNK platform. Request --- ## List Transactions(Endpoints) Retrieves a paginated list of transactions for a specific wallet. Supports filtering by `walletId` and optional date range. The date range can be applied to the `createdAt` or `updatedAt` timestamp, determined by the `filterMode`. If omitted, the `filterMode` defaults to `CREATED_AT`. Refer to the [Fiat payments guide](https://docs.bvnk.com/docs/listing-transactions). Request --- ## List Wallets(Endpoints) Retrieves a list of wallets on your account. Displays the first 10 wallets without max set to higher Request --- ## List Wallet Profiles(Endpoints) Returns available wallet profiles based on optional filters for currency codes and payment methods. --- For more information, how to apply it in the Embedded Wallets flow, refer to the [Assign a wallet profile](https://docs.bvnk.com/docs/step-2-creating-a-customer-wallet-and-virtual-account#/assign-a-wallet-profile) guide. Request --- ## Get Wallet(3) Retrieves detailed information about a specific wallet. Request --- ## Transactions Report V2(Endpoints) :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: Report all transactions from wallet in specified format. Report will be generated and sent to main account email in the specified format. Request --- ## Transactions Report(Endpoints) Creates a report of all transactions from a wallet in the specified format and sends it via the preferred delivery method: WEBHOOK or EMAIL. **Note**: Reports are delivered instantly but the data has a one-hour lag for Production environment and eight-hour delay in sandbox. For example, a report requested at 9:00 AM will include transactions only up to 8:00 AM. See the [Receive Transactions Report via Webhook](https://docs.bvnk.com/docs/receive-transaction-history-report-via-webhook) guide for more information. Request --- ## 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 /api/wallet`](../../api-explorer/endpoints/wallet-read) 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 `lsid` value. ```json Get wallets list [ { "id": 3683091, "description": "USDC Wallet", "currency": { "id": 1773, "code": "USDC", "fiat": true, "icon": null, "name": "Euro", "withdrawalParameters": [], "options": {}, "withdrawalFee": 0.4, "depositFee": 0, "supportsDeposits": true, "supportsWithdrawals": true, "quantityPrecision": 2, "pricePrecision": 5, "protocols": [] }, "lsid": "a:25041552279666:vpH2wtz:1", "status": "ACTIVE" } ] ``` 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: ```json Example Request Payload { "destinationWalletId": "a:25041552279666:vpH2wtz:1", "currency": "USDC" } ``` Here, "a:25041552279666:vpH2wtz:1" is `lsid` 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 `lsid` specified earlier must be listed here. ```json Wallets to Accept customer Fees { "content": [ { "destinationWalletId": "a:25041552279666:vpH2wtz:1", "currency": "USDC" }, { "destinationWalletId": "a:38472618394521:kLm9xRs:1", "currency": "USDC" } ], "pageable": { "pageNumber": 0, "pageSize": 20, "sort": [], "offset": 0, "paged": true, "unpaged": false }, "first": true, "last": true, "size": 20, "number": 0, "sort": [], "numberOfElements": 2, "empty": false } ``` 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": { "value": "100.00", "currency": "USD" }, "customerFee": { "value": "20.00", "currency": "USD" }, "paymentReference": "Ref112455", "instruction": { "type": "FIAT", "paymentMethod": "ACH", "beneficiary": { "details": { "beneficiaryType": "SELF_OWNED", "transferDestination": "LOCAL", "currency": "USD", "businessDetails": { "businessName": "Company ABC" }, "address": { "addressLine1": "Some address Line 1", "addressLine2": "Some address Line 2", "city": "Some city", "region": "Some region", "postCode": "ABCDEF", "country": "US" }, "bankDetails": { "accountNumber": "2152209165", "accountType": "CHECKING", "code": "021214891" } } } }, "requestDetails": { "originator": { "ipAddress": "5.57.72.118" } }, "metadata": { "source": "api", "merchantId": "merchantOne" } } } ``` 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. --- ## Individual requirements To successfully onboard consumers or individual customers to the BVNK platform, the following information is required: | Category | Field / Type | Required | Format / Supported values | Notes / Conditions | Example | | :--- | :--- | :---: | :--- | :--- | :--- | | **Personal Information** | First Name | ✅ | `String` | | John | | | Last Name | ✅ | `String` | | Syme | | | Date of Birth | ✅ | `YYYY-MM-DD` | | 1990-05-15 | | | Nationality | ✅ | `ISO 3166-1 Alpha-2` | | US | | | Country of Residence | ✅ | `ISO 3166-1 Alpha-2` | | US | | **Contact Information** | Email Address | ✅ | `Valid email address` | | [user@example.com](mailto:user@example.com) | | **Address Information** | Street Address | ✅ | `String` | | 221B Baker Street | | | City | ✅ | `String` | | Syracuse | | | Postal Code | ✅ | `String` | | 12345 | | | State | ⚠️ Conditional | `String` | **Mandatory for US residents** | New York | | | Country | ✅ | `ISO 3166-1 Alpha-2` | | US | | **Tax Identification** | Tax ID Number | ✅ | `String` | **Mandatory for US residents** | 123-45-6789 | | **Customer Due Diligence Information (CDD)** | Intended Use of Account | ✅ | `TRANSFERS_OWN_WALLET`, `TRANSFERS_FAMILY_FRIENDS`, `INVESTMENTS`, `GOODS_SERVICES`, `DONATIONS` | | `INVESTMENTS` | | | Employment Status | ✅ | `SELF-EMPLOYED`, `SALARIED`, `UNEMPLOYED`, `RETIRED`, `NOT_PROVIDED` | | `SALARIED` | | | Source of Funds | ✅ | `SALARY`, `PENSION`, `SAVINGS`, `SELF_EMPLOYMENT`, `CRYPTO_TRADING`, `GAMBLING`, `REAL_ESTATE` | | `SALARY` | | | PEP Status | ✅ | `NOT_PEP`, `FORMER_PEP_2_YEARS` (within 2 years), `FORMER_PEP_OLDER` (older than 2 years), `DOMESTIC_PEP`, `FOREIGN_PEP`, `CLOSE_ASSOCIATES`, `FAMILY_MEMBERS` | | `NOT_PEP` | | **Supported Documents** | Document Type | ✅ | `PASSPORT`, `ID_CARD`, `DRIVERS`, `RESIDENCE_PERMIT`, `SELFIE`, `PROOF_OF_RESIDENCE` (`UTILITY_BILL`) | Three types of documents must be provided | `PASSPORT` | | | Document SubType | ⚠️ Conditional | `FRONT_SIDE`, `BACK_SIDE` | `BACK_SIDE` required if applicable | `FRONT_SIDE` | :::warning Make sure the image of oneself (`SELFIE`) is included along with the ID. ::: :::warning Important For double-sided documents, always upload `"subType": "FRONT_SIDE"` before `"subType": "BACK_SIDE"`. Verification cannot be completed unless both sides are submitted. ::: To streamline the onboarding process for individual customers, it's essential to submit identity documents that meet the following requirements: **Document requirements**: * The document must be valid and not expired. * It must be free from visible damage such as scratches, stains, or tears. * The applicant's full name, date of birth, MRZ (Machine Readable Zone), and other key details must be clearly visible. * The document must belong to the individual being onboarded. **An ID document must include**: * Full name, date of birth, a clear photograph, and (if applicable) a signature. * A unique document number. * Validity details such as the issue date or expiry date. **Photo requirements**: * The document image must be a high-quality original photo or scan (not a screenshot or an image from social media), in JPG, JPEG, PNG, or PDF format. * If the document has information on both sides, images of both sides must be provided. * The image must be in color, with a minimum resolution of 300 DPI or file size of at least 100 KB. * All document details must be readable. * The entire document, including all corners, must be visible—no cropping, obstructions, or foreign elements. * The image must not be digitally altered or edited. * Digital documents are generally not accepted unless explicitly allowed. --- ## Business requirements To onboard a business Enterprise Payment customer onto our platform, we must collect and verify essential information regarding the business and its ownership. This is a regulatory requirement and a critical component of our risk management process. The onboarding process typically involves a collaborative review by our Compliance and Onboarding teams to ensure full adherence to Know Your Business (KYB) standards. The documentation and data we collect are categorized as follows: * **Baseline requirements**. These apply to all business customers, regardless of their industry or vertical. * **Vertical-specific requirements**. Additional documentation or information may be required based on the customer's specific industry or risk profile. ## Baseline Requirements All documents must be provided via the [`POST customers/{customerReference}/documents`](../../../api-explorer/endpoints/upload-customer-documents) endpoint or via the Portal (if applicable). The following requirements apply to all Business customers: ### Company Data * Legal Entity name * Legal Entity Registration Number * Legal Entity Type * Legal Entity Country of Incorporation * Legal Entity Registration number (EIN/TIN for US) * Legal Entity Industry * Legal Entity Registered address * Legal Entity Operating address (if applicable) ### Company Associated parties (associates) :::info At least one Representative, UBO, and Director must be specified. The same person can share multiple roles. ::: * Title (REPRESENTATIVE / UBO / DIRECTOR) * First name (include middle Name(s) as well) * Last name * Date of birth * Nationality * Address details * Email contact * SSN / ITIN (U.S. only) * ID Document (Non-U.S.) * Document Type * Unique Number * Expiration Date * Issuing Country ## Supported document types and subtypes The following `Type`and`SubType` fields are supported for Business verification: | Type | SubType | Description | | ------------- | :----------------------- | ------------------------------------------------------------------------------ | | `COMPANY_DOC` | `DIRECTORS_REGISTRY` | Directors' registry or equivalent proof of directorship. | | `COMPANY_DOC` | `INCORPORATION_ARTICLES` | Memorandum/articles of incorporation/association/registration. | | `COMPANY_DOC` | `INCORPORATION_CERT` | Certificate of incorporation/registration. | | `COMPANY_DOC` | `PROOF_OF_ADDRESS` | Proof of address (e.g., utility bill, lease contract). | | `COMPANY_DOC` | `SHAREHOLDER_REGISTRY` | Current corporate/group structure or shareholder registry. | | `COMPANY_DOC` | `OTHER` | Evidence of Source of Funds (bank statements, audited financials). | | `COMPANY_DOC` | `OTHER` | Evidence of Source of Wealth (e.g., tax returns, sale of assets, investments). | ## Vertical-specific requirements Vertical or Industry-specific requirements include the following: | Type | SubType | Description | | :------------ | :--------------------- | :------------------------------------------------------------ | | `COMPANY_DOC` | `OTHER` | Document `type` and `subtype` for all the cases listed below. | | `COMPANY_DOC` | `SHAREHOLDER_REGISTRY` | Document `type` and `subtype` for all the cases listed below. | * AML Policy * Proof of Regulatory License, for example, EMI/PI license, FCA registration * Wolfsberg Questionnaire * Proof of Business Activity, for example, client contracts, invoices * Shareholder ownership. `"subType": "SHAREHOLDER_REGISTRY"` * Proof of Regulatory License, for example, EMI/PI/Banking license * AML Policy * Proof of Business Activity, for example, gambling operator license, evidence of active business * Proof of Regulatory License, for example, Gambling Commission license * Shareholder ownership. `"subType": "SHAREHOLDER_REGISTRY"` * AML Policy * Proof of Regulatory License, for example, VASP/Exchange license ## Enhanced Due Diligence (EDD) The following Enhanced Due Diligence (EDD) checks are triggered for high-risk jurisdictions, industries, or adverse media hits: * Independent audit letter * Legal opinion on structure (if complex) * Ongoing monitoring procedures --- ## Add documents to customers Make sure to study [Compliance Requirements](../../compliance-requirements/embedded-compliance-requirements-businesses) before submitting the corresponding documents. ## Add documents to a Company customer Each request can upload multiple documents simultaneously. :::warning When uploading customer documents: * Reference the correct `customerReference`. * Use `customerPersonReference` when the document belongs to a specific associate. * If `customerPersonReference`is omitted or `null`, the document is attached at the company level for business customers. * Encode files to Base64 before sending. ::: To add documents to a customer profile, send the [`POST customers/{customerReference}/documents` ](../../../api-explorer/endpoints/upload-customer-documents) request with the `customerReference` in the path and the following request body: ```json Example: upload multiple documents for a company and an associate [ { "type": "COMPANY_DOC", "subType": "INCORPORATION_CERT", "name": "Company Structure.pdf", "description": "Document optional description", "countryCode": "GB", "externalReference": "2b8d8bde-eef5-408a-9228-96bef24865ad", "content": "JVBERi0xLjUKJeLjz9MKMSAwIG9iago8PC9UeXBlIC9QYWdl..." }, { "customerPersonReference": "9b2c3d4e-567f-48f0-abc1-03a4e3df12ab", "type": "PASSPORT", "subType": "FRONT_SIDE", "name": "John Smith Passport.pdf", "description": "Document optional description", "countryCode": "GB", "externalReference": "2b8d8bde-eef5-408a-9228-96bef24865ad", "content": "JVBERi0xLjUKJeLjz9MKMSAwIG9iago8PC9UeXBlIC9QYWdl..." } ] ``` | Parameter | Required | Description | | :------------------ | :-------: | :------------------------------------- | | `customerReference` | :white_check_mark: | The unique identifier of the customer to whom the documents will be attached. Customer has to be in `INFO_REQUIRED` status (status after agreement consent) | | Attribute | Required | Description | | :--- | :---: | :--- | | `customerPersonReference` | :x: | Reference that links the document to a specific **associate** (for example, beneficial owner). If omitted or null, the document is attached at the company level. | | `type` | :white_check_mark: | The document type, for example COMPANY\_DOC.See the full list of supported [COMPANY document type](../../compliance-requirements/embedded-compliance-requirements-businesses).When uploading documents for an **associate** (e.g. ) refer to the following [list for supported document types](../../compliance-requirements/compliance-requirements-for-individuals2). | | `subType` | :x: | Additional specification of the document type, e.g. FRONT\_SIDE, BACK\_SIDE if applicable. See the full list of supported [document subType for COMPANY documents](../../compliance-requirements/embedded-compliance-requirements-businesses).When uploading documents for an **associate** (e.g. PASSPORT) refer to the following [list for supported document subTypes](../../compliance-requirements/compliance-requirements-for-individuals2). | | `name` | :white_check_mark: | Optional human-readable filename, for example, "John Smith Passport.pdf". | | `description` | :x: | Optional description for the provided document | | `countryCode` | :white_check_mark: | ISO 3166-1 alpha-2 country code associated with the document. | | `externalReference` | :white_check_mark: | The internal reference mapped to the document from your end. | | `content` | :white_check_mark: | Base64-encoded file content. | ```json Example: upload multiple documents for an individual [ { "type": "PASSPORT", "subType": "FRONT_SIDE", "name": "John Smith Passport.pdf", "description": "Document optional description", "countryCode": "GB", "externalReference": "2b8d8bde-eef5-408a-9228-96bef24865ad", "content": "JVBERi0xLjUKJeLjz9MKMSAwIG9iago8PC9UeXBlIC9QYWdl..." } ] ``` | Parameter | Type | Required | Description | | :------------------ | :------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | | `customerReference` | `string` | Yes | The unique identifier of the customer to whom the documents will be attached. Customer has to be in `INFO_REQUIRED` status (status after agreement consent) | | Attribute | Type | Required | Description | | :--- | :--- | :--- | :--- | | `type` | `string` | Yes | You must provide all three types of documents:ID (`PASSPORT`, `ID_CARD`, `DRIVER_LICENSE`, `RESIDENCE_PERMIT`)`SELFIE` (must be uploaded along with any ID)`PROOF_OF_RESIDENCE` (`UTILITY_BILL`) For the full list of supported document types, refer to the [Compliance Requirements for Individuals](../../compliance-requirements/compliance-requirements-for-individuals2). | | `subType` | `string` | No | Additional specification of the document type, e.g. `FRONT_SIDE`, `BACK_SIDE` if applicable. [Full list of supported document subTypes](../../compliance-requirements/compliance-requirements-for-individuals2) | | `name` | `string(128)` | Yes | Human-readable filename, for example, `"John Smith Passport.pdf"`. | | `description` | `string(255)` | No | Optional description for the provided document | | `countryCode` | `string(2)` | Yes | ISO 3166-1 alpha-2 country code associated with the document. | | `externalReference` | `string(36)` | Yes | The external reference mapped to the document from your end. | | `content` | `string` | Yes | Base64-encoded file content. | In the successful response, you receive unique references for tracking the status. That means the request has been accepted and the documents are queued for upload. ```json Success { "reference": "d42e1c62-27b8-4b3b-b51e-eb10edeb1731", "externalReference": "2b8d8bde-eef5-408a-9228-96bef24865ad", "status": "INIT" } ``` ```json Failure { "code": "DOCUMENTS-4000", "status": "BAD_REQUEST", "message": "Invalid document content", "details": { "errors": { "content": [ "Document file content is not valid base64" ] } } } ``` ## Manage documents :::info Tips * **Check Document Status**. Retrieve the customer or associated person object to confirm document verification status. * **Retain Document References**. Store the `reference` returned in the success response for future queries or audits. * **Combine with KYC**. Document verification may be required before the customer status changes from `PENDING` to `VERIFIED`. ::: ### Search Documents To search for specific documents, send the [`GET /platform/v1/customers/documents`](../../../api-explorer/endpoints/get-customer-documents) request with the optional query parameters: * `customerReference` * `externalReference` * `name` * `types` * `subTypes` * `statuses` (e.g., INIT, PENDING, APPROVED, DECLINED) ### Get document download URL To get a Document Download link, send the [`GET /platform/v1/customers/documents/{documentReference}/url`](../../../api-explorer/endpoints/get-document-url) request with the `{documentReference}` specified in the query. In the successful response, you receive a temporary URL for document download. ### Delete document To remove a document from a customer's profile, send the [`DELETE /platform/v1/customers/documents/{documentReference}`](../../../api-explorer/endpoints/delete-document) request with the `{documentReference}` specified in the query. In the successful response, you receive the status `204 No Content`, which means the document is deleted. ## Complete onboarding :::warning This feature will be discontinued and deprecated after January 31, 2026. The endpoint is no longer used in the onboarding flow. Now, onboarding procedures are completed automatically once you provide all the required documents and meet the customer compliance requirements. ::: After uploading all required documentation for a customer, wait ~10 seconds for the system to acknowledge the documents. Then, confirm the completion of the onboarding process. For that, send the [`POST /platform/v1/customers/{customerReference}/complete-onboarding`](../../../api-explorer/endpoints/complete-onboarding) request with `reference` as the path parameter. | Parameter | Required | Description | | ------------------- | :--------: | ---------------------------------------------------------------------------------------------------------- | | `customerReference` | :white_check_mark: | The unique reference identifier for the customer provided during customer creation. | When you successfully call this endpoint: * BVNK will initiate final compliance and verification checks. * In the sandbox environment, customer verification is automatically approved. * In production, if the customer meets all compliance requirements, the status will update to `VERIFIED`. If there are outstanding requirements or issues, the status will move to `INFO_REQUIRED` or `REJECTED`, prompting manual review or further actions. ## Check monitoring status You can monitor the customer's verification status via: * **API**: Retrieve current customer's onboarding status by sending the [`GET /platform/v1/customers/{customerReference}`](../../../api-explorer/endpoints/get-customer-with-external-status) request. * **Portal**: Track progress and statuses directly through the BVNK Portal. * **Webhooks**: [Document verification status changes](../../../api-explorer/bvnk-webhooks/customer-document-status-change). --- **What's next?** After you create and onboard your customers, you are ready to send and receive payments. - See the available [use cases](../find-your-usecase.mdx) to select the one that best suits your needs. - [Create a wallet](../creating-your-first-wallet.mdx) for yourself or your customers. - If you are onboarding a Business customer, you also need to submit the [questionnaires](../onboard-epc) for them. --- ## Create a customer The following guide describes how you can create **company** or **individual** customer accounts on the BVNK platform. **Partner** is a business that BVNK onboards that via a technical integration will embed some elements of our product into their platform. **Customer** is both the partner's customer (business or individuals) and BVNK's customer. BVNK provides payment services to the customers. Once a customer is created, we initiate verification. You can then retrieve the customer object or listen to webhooks to track the status change. ## Prerequisites * Collect and validate customer data (IDs, address proofs, and so on) before creating the customer. This is crucial for a smooth KYC/KYB flow. * Remember to obtain the verified `reference` from the [signed agreement session](../signing-customer-agreements) and include it in the request payload as the `signedAgreementSessionReference` field. This ensures the agreements are correctly linked and recognized in your new customer creation and onboarding. * Learn about required compliance documents for [company](../../compliance-requirements/embedded-compliance-requirements-businesses) and [individual customers](../../compliance-requirements/compliance-requirements-for-individuals2) and add them for your customer. ## Add a customer This step allows the creation of a new customer on the BVNK platform to whom wallets can be assigned. For example, if your company is a financial institution that services businesses, you would first need to add the customer under the **My customer** section of the BVNK platform. This will facilitate your customer's execution of payments via BVNK through your platform. To add a customer: Select a type of customer you want to create: Use this endpoint to create a business or company customer. You can associate multiple beneficial owners, controlling persons, and signers (often referred to collectively as "associates"). To create a customer type: Company, send the `POST /platform/v1/customers` request. For the detailed request body parameters, see the [Create customer](../../../api-explorer/endpoints/create-customer) endpoint. Upon a successful response, you receive a unique customer reference for tracking the verification status. ```json Success { "reference": "b0ef182b-202d-4365-b69d-98dcb225fd62", "status": "INFO_REQUIRED", "infoRequired": { "questionnaireCodes": ["financialServicesQuestionnaireFull"] } } ``` ```json Failure { "code": "ACCOUNTS-2000", "status": "BAD_REQUEST", "message": "Invalid request", "details": { "errors": { "industryReference": [ "Industry with reference: 56e65ebc-06fa-11ef-bbf8-02d3d923cf2b does not exist" ] } } } ``` | Attribute | Type | Description | | --------- | ------ | -------------| | `reference` | string | A unique identifier for the customer. | | `status` | string | The current status of the customer. Possible values include:• `INFO_REQUIRED` – customer information is needed for verification. Note that in this case, the code for a related questionnaire is sent. For more information, see [Retrieve Compliance Data](../onboard-epc) • `PENDING` – pending customer verification• `VERIFIED` – customer has been verified• `REJECTED` – verification has been unsuccessful. | Use the following endpoint to create a customer type: "Individual". Prerequisites: * Ensure accurate personal data (name, DOB, nationality, address) is collected to facilitate a smooth KYC process. * Collect information about the customer before proceeding with the creation request. * Customer Due Diligence (CDD) data is required. To create an individual customer, send the `POST /platform/v1/customers` request. For the detailed request body parameters, see the [Create customer](../../../api-explorer/endpoints/create-customer) endpoint. In the successful response, you receive a unique customer `reference` for tracking the verification status. ```json Success { "reference": "b0ef182b-202d-4365-b69d-98dcb225fd62", "status": "INFO_REQUIRED", } ``` ```json Failure { "code": "ACCOUNTS-2000", "status": "BAD_REQUEST", "message": "Invalid request", "details": { "errors": { "signedAgreementSessionReference": [ "Agreement session with reference: 814d949b-9a6d-49ea-92f5-23ee51b37e24 is already assigned to the customer" ] } } } ``` 1. Log in to your account on the BVNK Portal. ![](/img/bvnk/get-started/portal-login.png) 2. Go to **My customers** tab. ![](/img/bvnk/get-started/my-customers-tab.png) 3. Click **Add New Customer**. ![](/img/bvnk/get-started/add-new-customer.png) Fill out the customer information form with the following details: * Business Name * Business Description * Business Industry * Country of Incorporation * Estimated Monthly Volume * Business Risk Score (Low, Medium, High) * Use Case 4. Verify the entered information for accuracy.\ Click **Continue** > **Confirm** to register the customer or **Cancel** to make changes. ![](/img/bvnk/get-started/customer-confirmation.png) ### Acquire agreement consent After submission, the customer status will change to "Agreements Pending". This means your customer must sign BVNK's Terms of Service. In the customer-side panel view, copy and share the agreements link with your customer. ![](/img/bvnk/get-started/agreements-link.png) To proceed, the customer must consent to BVNK's Terms of Service. ![](/img/bvnk/get-started/terms-consent.png) ### Verify submitted customer data Once consent is provided, the agreement status will automatically update to SIGNED, enabling the next step: customer verification. ![](/img/bvnk/get-started/verification-status.png) The verification process ensures compliance with BVNK's Due Diligence requirements and is essential for activating Embedded Wallets. The required information and documents for the Know Your Business (KYB) process are provided via a hosted experience accessible via the verification link. To start customer verification, in the customer-side panel view, copy the verification link to access BVNK's KYB/KYC process and share it with your customer. ![](/img/bvnk/get-started/verification-link.png) If additional data is required, the KYB/C verification link allows you, as a partner, to provide the necessary customer documents or information. Customer information includes, but is not limited to, the following: * Personal or business details: for example, name, email, and address. * Unique identifiers such as Social Security Number (SSN), Tax Identification Number (TIN), business registration number, and so on. * Supporting documents, such as proof of identity, proof of address, or business registration certificates. ### Complete verification BVNK performs Know Your Customer (KYC) and Know Your Business (KYB) checks to verify customer identity and legitimacy. Once the verification process is complete, the customer's status is updated in the BVNK's system. Approved customers can immediately access payment services and automatically have wallets assigned to them. ![](/img/bvnk/get-started/verification-complete.png) If the verification fails, customers may be asked to provide additional information or documentation. ## Customer webhooks To get notified when the status of the customer changes, listen to the following webhooks: - [Customer's status changes to `INFO_REQUIRED`, `PENDING`, `VERIFIED`, or `REJECTED`](../../../api-explorer/bvnk-webhooks/customer-status-change). - [Customer information is updated](../../../api-explorer/bvnk-webhooks/customer-update). --- **What's next?** * [Add compliance documents to a customer](../add-documents-to-customers) * [Onboard a customer](../onboard-epc) * [Retrieve information about a customer](../retrieve-embedded-partner-customer) --- ## Create a customer(Create-embedded-partner-customer) Embedded Wallets provide partners with seamless payment and fund management capabilities directly within their platform while maintaining their branding. This product is available to businesses and individual customers based in the US, UK, EEA, and other countries supported by BVNK outside of these regions. To get started: 1. **Initiate the activation process**.\ The partner representative must contact their BVNK Account Manager to initiate the activation process. 2. **Complete compliance procedures**.\ Once compliance reviews are complete, the Embedded Wallets product will be enabled on the BVNK Portal and accessible via API. Following this, you can create customer accounts. 3. **Onboard your first customer**. ## API environments You can use separate environments for development and live operations: | Environment | Location | | :------------- | :--------------------------------------- | | **Production** | `https://api.bvnk.com/` | | **Sandbox** | `https://api.sandbox.bvnk.com/` | Use the sandbox environment to test your integration safely before going live. --- ## Gather compliance data for onboarding :::info Only required for the Company customers onboarding ::: To complete the onboarding of new customers, you must provide their details for the KYC/KYB checks. To collect the information, Questionnaires with industry-related questions are used. The system determines which questionnaire each customer needs to complete. The necessary questionnaires are listed in the `infoRequired.questionnaires` field of the `GET /platform/v1/customers/{reference}` endpoint. The high-level flow of the process is the following: 1. Partner fetches questionnaire schema. 2. Partner gathers and submits the customer's answers for verification. 3. BVNK validates whether all answers are provided: 1. If YES: Customer transitions to the next onboarding state. 2. If NO: BVNK specifies missing fields. Partner is requested to provide missing answers until all required details are submitted. ## Retrieve questionnaires To retrieve all existing questionnaires, including sections, field types, and required inputs, send the [`GET /platform/v1/customers/questionnaire/definitions`](../../../api-explorer/endpoints/get-questionnaire-definitions) request. To retrieve specific questionnaires, send the [`GET /platform/v1/customers/questionnaire/definitions`](../../../api-explorer/endpoints/get-questionnaire-definitions) request with industry `codes` specified in the path. You can use the following industry-related `codes` to acquire a required questionnaire: * Financial Services: `financialServicesQuestionnaireFull` * Digital assets/crypto: `amlCryptoComplianceQuestionnaireFull` * Gaming: `gamblingQuestionnaireFull` * Fintech: `fintechsQuestionnaireFull` * Other (unspecified): `otherQuestionnaireFull` :::info If you don't specify `codes`, all available questionnaires will be loaded. ::: The response includes the required questions and types of answers that are expected to be provided. You can integrate the acquired questionnaire into your app or website front end.
Example response ```json [ { "code": "amlCryptoComplianceQuestionnaireFull", "title": "Digital Assets (Crypto) Questionnaire", "description": "This questionnaire assesses AML/CFT and crypto compliance practices.", "sections": [ { "code": "nature_of_business_section", "title": "Nature of Business", "items": [ { "code": "products_services_offered", "title": "What products or services does your company offer?", "type": "textArea", "required": true }, { "code": "clients_served", "title": "What types of clients do you plan to service through BVNK?", "type": "selectDropdown", "required": true, "options": [ { "value": "individuals", "title": "Individuals" }, { "value": "corporates", "title": "Corporates" }, { "value": "both", "title": "Both" } ] }, { "code": "exposure_prohibited_jurisdictions", "title": "Exposure to prohibited jurisdictions?", "type": "selectDropdown", "required": true, "options": [ { "value": "yes", "title": "Yes", "score": 10 }, { "value": "no", "title": "No", "score": 0 } ] } ] }, { "code": "documentRequirementsGeneral", "title": "Document requirements", "items": [ { "code": "certificateOfIncorpo", "title": "Certificate of Incorporation", "type": "fileAttachment" }, { "code": "amlPolicy", "title": "AML Policy", "type": "fileAttachment" } ] }, { "code": "amlCompliance", "title": "AML/CFT Compliance", "items": [ { "code": "travelRule", "title": "Is your company required to comply with the Travel Rule?", "type": "selectDropdown", "required": true, "options": [ { "value": "yes", "title": "Yes" }, { "value": "no", "title": "No" } ] } ] } ] }, { "code": "financialServicesQuestionnaireFull", "title": "Financial Services Questionnaire", "sections": [ { "code": "nature_of_business_section", "title": "Nature of Business", "items": [ { "code": "products_services_offered", "title": "What products or services does your company offer?", "type": "textArea", "required": true } ] }, { "code": "clientClassification", "title": "Client Classification", "items": [ { "code": "clientType", "title": "Does your company service retail or professional clients?", "type": "selectDropdown", "required": true, "options": [ { "value": "retail", "title": "Retail" }, { "value": "professional", "title": "Professional" }, { "value": "both", "title": "Both" } ] } ] } ] } ] ```
## Submit questionnaire responses After the customer answers all the questions, the questionnaire must be submitted for analysis and verification. :::warning Ensure that the customer provides the most comprehensive and complete answers to **all required** questions. Otherwise, the onboarding can be put on hold until BVNK receives the remaining details. ::: To submit completed questionnaire responses for a specific customer, send the [`PUT /platform/v1/customers/{customerReference}/questionnaires`](../../../api-explorer/endpoints/submit-questionnaire-responses) request with `customerReference` specified in the path. In the body, specify the following parameters: ```json Example request [ { "code": "amlCryptoComplianceQuestionnaireFull", "sections": [ { "code": "nature_of_business_section", "items": [ { "code": "products_services_offered", "value": "embedded" } ] } ] } ] ``` | Field | Description | |-------|-------------| | `code` | Industry-specific code of the questionnaire | | `sections` | A list of questionnaires, each with its code and item-level answers | | `sections.code` | Unique code of a section | | `items.code` | Unique code of a question | | `items.value` | Customer's answer in the form of string, dropdown value, or file reference ID | In the successful response, you receive a message that the questionnaire has been submitted. ```json Example response { "status": "success", "message": "Questionnaire submitted successfully." } ``` ## Search questionnaire submissions To search for previously submitted questionnaire responses, send the [`GET /platform/v1/customers/questionnaires`](../../../api-explorer/endpoints/get-questionnaires) request with the following query parameters: | Field | Description | |-------|-------------| | `customerReference` | Customer reference | | `codes` | Questionnaire identifiers | | `statuses` | Status filter (INIT, OK, NOK) | ```bash GET /platform/v1/customers/questionnaires?customerReference=52ec68a9-1ff1-4b7c-a8b5-0630de484e42\&codes=amlCryptoComplianceQuestionnaireFull\&statuses=INIT,OK,NOK ``` In the successful response, you receive the submitted questionnaires for a specified customer based on the added filters ```json Example response [ { "customerReference": "52ec68a9-1ff1-4b7c-a8b5-0630de484e42", "questionnaireCode": "amlCryptoComplianceQuestionnaireFull", "status": "OK", "submittedDate": "2025-05-14T14:45:00Z", "sections": [ { "code": "nature_of_business_section", "items": [ { "code": "products_services_offered", "value": "embedded" } ] } ] } ] ``` --- ## Reliance Onboarding :::warning This page is a work in progress. Please treat this article with caution—it's here to demonstrate a flow that's currently being developed. Endpoint and field names might change or be removed as things evolve. ::: The **Reliance Onboarding** model offers a controlled alternative KYC intake path. Eligible partners act as the primary identity verification provider, transmitting pre-verified PII, CDD data, and verification metadata to BVNK via API. During the standard flow, customers are supposed to provide their documents, which BVNK later sends for verification to a designated entity. The new Reliance model changes this approach in the following way: * You conduct identity verification on your side in accordance with the agreed standards and provide the required verification outcomes to BVNK. No ID documents, selfies, or proofs of address are uploaded at this stage, but all submitted information is subject to validation. * You still have to provide customer data, such as name, date of birth, PEP status, and so on, via API. To onboard a customer within the Reliance model, do the following: 1. Prepare and sign agreements with your customer as described in the [guide](./signing-customer-agreements). 2. Create an **Individual** customer by sending the [`POST /api/platform/v1/customers`](../../api-explorer/endpoints/create-customer) request with their personal and compliance data. ```json Example Request { "type": "INDIVIDUAL", "individual": { "description": "Tier 1 customer", "firstName": "John", "lastName": "Dole", "dateOfBirth": "1990-05-12", "nationality": "GB", // Required for EU "birthCountryCode": "GB", // Required for EU "emailAddress": "john.dole@example.com", "address": { "addressLine1": "123 Main St", "city": "New York", "postalCode": "10001", "stateCode": "NY", "countryCode": "US" }, "taxIdentification": { // Required for US residents "number": "123-45-6789", "taxResidenceCountryCode": "US" }, "cdd": { "employmentStatus": "SALARIED", "sourceOfFunds": "CRYPTO_TRADING", "pepStatus": "NOT_PEP", "intendedUseOfAccount": "INVESTMENTS", "expectedMonthlyVolume": { // Required for EU "amount": "500.01", "currency": "EUR" }, "estimatedYearlyIncome": "INCOME_0_TO_50K", // Required for US residents "employmentIndustrySector": "AGRICULTURE_FORESTRY_FISHING_HUNTING" // Required for US residents }, "reliance": { "identityDocumentMetadata": { "type": "PASSPORT", "subType": "FRONT_SIDE", "number": "AB1234567", "issueDate": "2020-12-31", "expiryDate": "2030-12-31", "issuingCountryCode": "GB", "kycTimestamp": "2025-11-20T15:30:45.123Z" }, "biometricLiveness": { // Required for EU "livenessTimestamp": "2025-11-20T15:30:45.123Z" }, "addressVerification": { "type": "DOC" }, "attestation": { "riskScore": "LOW", "eddCompleted": true } } }, "signedAgreementSessionReference": "78eedde1-2402-4a59-8bbd-ccecb6d612d1" } ``` For the detailed descriptions and examples of each field, see the [endpoint](../../api-explorer/endpoints/create-customer) in the API Reference and the [Create a customer via API](./creating-an-embedded-customer-company-api) guide. Make sure to include the following parameters in the payload to submit metadata for the proofing documents you have already verified, including document ID, type, and timestamps. :::warning Data requirements differ for EU and US entities. Make sure to provide the correct data for the corresponding region. ::: | Parameter | Description | | --------- | ----------- | | `identityDocumentMetadata` | Details of the ID document you verified (Type, Number, Expiry, Issuing Country).Not required for US residents when a Social Security number is provided. | |`identityDocumentMetadata.expiryDate`|Expiration date of the provided document. Mandatory for all document types, except `ID_CARD` and `OTHER`.| | `biometricLiveness` | **Mandatory for EU entities**. Timestamp for biometric or liveness checks performed on your side. | | `addressVerification` | The method you used to verify the residential address.Values:* DOC: verified by a document* NON_DOC: verified via a database or registry | | `riskScore` | Your internal risk rating for this customer per Personally Identifiable Information (PII).Allowed values:* LOW* MEDIUM* HIGH | | `eddCompleted` | Flag to indicate that you have already performed Enhanced Due Diligence (EDD).BVNK doesn't verify customer attestations within this flow, so make sure you have completed the EDD.Allowed values:* True* False | In the successful response, you receive the status `200 OK`, which means the customer is created. The customer status is set to `PENDING`, indicating that the required data has already been provided and that BVNK can run internal checks. :::warning Any attempt to follow the standard onboarding flow by sending the [`POST /platform/v1/customers/{customerReference}/documents`](../../api-explorer/endpoints/upload-customer-documents) request will be rejected. ::: After you submit the customer's data, the API returns a status indicating the result of the onboarding request: | Status | Description | Action Required | | -------------- | ------------------------------------------------ | ------------------------------------------------------------------------------ | | `VERIFIED` | The customer application is approved. | None. Onboarding is automatically finalised. | | `PENDING` | The customer application is under manual review. | Monitor the status via webhooks. | | `EDD_REQUIRED` | BVNK requires further checks. | An internal case is raised for manual review. Monitor the status via webhooks. | You can monitor the customer's verification status via the following endpoints: * API: Retrieve current customer's onboarding status by sending the [`GET /platform/v1/customers/{customerReference}`](../../api-explorer/endpoints/get-customer-with-external-status) request. * Portal: Track progress and statuses directly through the BVNK Portal. * Webhooks: Set up webhooks to receive automated status change notifications. The **Partner-Managed Onboarding** model for Businesses (KYB) uses a hybrid approach to maximize speed while maintaining compliance. * Associates (Directors/UBOs): You perform Identity Verification on your side and send verification metadata via API. You do not need to upload any ID or selfie documents. * The Entity (Company): You upload the required legal documents, for example, the Certificate of Incorporation, via the documents endpoint. * Risk: You attest to the risk scoring and screening results. To onboard a business customer within the Partner-Managed model, do the following: 1. Prepare and sign agreements with your customer as described in the [guide](./signing-customer-agreements). 2. Create a **Business** customer by sending the [`POST /platform/v1/customers`](../../api-explorer/endpoints/create-customer) request with `"type": "COMPANY"`. ```json Example Request { "type": "COMPANY", "company": { "name": "Acme Corp Ltd", "entityType": "LIMITED_LIABILITY_COMPANY", "registrationNumber": "12345678", "incorporationDate": "2015-06-20", "incorporationCountryCode": "GB", "taxResidenceCountryCode": "GB", "industryReference": "30ea5818-06d8-11ef-b6e1-027612b7f6b5", // UUID from Industry API "taxIdentification": { "number": "12-3456789", // Mandatory for US (EIN) "taxResidenceCountryCode": "US" }, "address": { "addressLine1": "456 Corporate Blvd", "city": "London", "postalCode": "EC1A 1BB", "countryCode": "GB" }, "cdd": { "intendedUseOfAccount": "PAYROLL", "pepStatus": "NOT_PEP", "expectedMonthlyVolume": { "amount": "50000.00", "currency": "EUR" } }, "reliance": { // NOTE: Incorporation, Address, and SoF are handled via /documents in Step 3. "attestation": { "riskScore": "LOW", "eddCompleted": true } }, "associates": [ { "titles": ["BUSINESS_OWNER", "DIRECTOR"], "person": { "firstName": "Jane", "lastName": "Doe", "dateOfBirth": "1985-03-15", "nationality": "GB", // Mandatory for EU "birthCountryCode": "GB", // Mandatory for EU "emailAddress": "jane@acme.com", "address": { "addressLine1": "10 Downing St", "city": "London", "postalCode": "SW1A 2AA", "countryCode": "GB" }, "reliance": { "identityDocumentMetadata": { "type": "PASSPORT", "number": "987654321", "expiryDate": "2031-01-01", // Optional for type: OTHER, ID_CARD "issueDate": "2021-01-01", // Optional "issuingCountryCode": "GB", "kycTimestamp": "2025-11-20T15:30:45.123Z" }, "biometricLiveness": { "livenessTimestamp": "2025-11-20T15:30:45.123Z" // Mandatory for EU }, "addressVerification": { "type": "DOC" } } } } ] }, "signedAgreementSessionReference": "78eedde1-2402-4a59-8bbd-ccecb6d612d1" } ``` For the detailed descriptions and examples of each field, see the [endpoint](../../api-explorer/endpoints/create-customer) in the API Reference. Crucial difference from the individual onboarding flow: * **Associates:** Do not trigger the document loop. Instead, include `reliance` metadata inside the `associates` block. * **Entity:** Include the `attestation` and `cdd` data, but exclude the company file proofs (Incorporation, Address, SoF) from the JSON. You will upload them in Step 3. :::warning Data requirements differ for EU and US entities. Make sure to provide the correct data for the corresponding region. ::: | **Parameter** | **Description** | | --------- | ----------- | |`company.taxIdentification.number`| Tax identification number of the entity:*US: EIN*EU: VAT number. | |`company.registrationNumber`| Registration number of the entity:*US: Optional*EU: Mandatory. | |`company.address.stateCode`| State code of the entity:*US: Mandatory*EU: Optional. | |`associates[].person.reliance.nationality`| Nationality of the associate:*US: Optional*EU: Mandatory ISO code. | |**Required to confirm you have screened the entity**.|| | `company.reliance.riskScore` | Your internal risk rating for this customer. Allowed values: `LOW`, `MEDIUM`, `HIGH` | | `company.reliance.eddCompleted` | Flag to indicate that you have already performed Enhanced Due Diligence (EDD). Allowed values: `true`, `false` | |**Required to bypass Associate ID uploads**.|| | `associates[].person.reliance.identityDocumentMetadata` | Details of the ID document you verified: `type`, `number`, `expiryDate`, `issuingCountryCode`, `kycTimestamp`. | Optional for US residents if SSN is provided. | |`associates[].person.reliance.biometricLiveness`| Timestamp for biometric or liveness checks performed on your side:*US: Optional*EU: Mandatory `livenessTimestamp`. | | `associates[].person.reliance.addressVerification` | The method you used to verify the residential address. Values: `DOC`, `NON_DOC` | | 3. After creating the customer, upload entity documents by sending the [`POST /platform/v1/customers/{customerReference}/documents`](../../api-explorer/endpoints/upload-customer-documents) request. The customer's status changes to `INFO_REQUIRED` until the entity documents are received. :::warning Do **NOT** upload documents for the Associates (Directors/UBOs). Their verification was handled via metadata in Step 2. ::: | Verification requirement | Document type to upload | Notes | | ------------------------ | ----------------------- | ----- | | **Certificate of Incorporation** | `COMPANY_DOC` > `INCORPORATION_CERT` | Mandatory for legal entity verification. **US entities only**. | | **Registered Address Proof** | `COMPANY_DOC` > `PROOF_OF_ADDRESS` | Utility bill, Bank Statement, or Tax Document (dated < 3 months). **EU entities only**. | | **Source of Funds** | `COMPANY_DOC` > `OTHER` | Bank Statement or Financials showing the entity's funding source. | ```json Example Request: Upload the Certificate of Incorporation { "type": "COMPANY_DOC", "subType": "INCORPORATION_CERT", "name": "INC_CERT.pdf", "description": "Business Certificate of Incorporation", "countryCode": "US", "externalReference": "{{$randomUUID}}", "content": "JVBERi0xLjQKJdPr6eEKMSAwIG9iago8PC9UaXRsZSAoVGVzdGlu[...]" } ``` Once the JSON is sent and the entity documents are uploaded, onboarding is completed. --- After you submit the customer's data and documents, the API returns a status indicating the result of the onboarding request: | Status | Description | Action Required | | -------------- | ------------------------------------------------ | ------------------------------------------------------------------------------ | | `VERIFIED` | The customer application is approved. | None. Onboarding is automatically finalised. | | `PENDING` | The customer application is under manual review. | Monitor the status via webhooks. | | `EDD_REQUIRED` | BVNK requires further checks. | An internal case is raised for manual review. Monitor the status via webhooks. | You can monitor the customer's verification status via the following endpoints: * API: Retrieve current customer's onboarding status by sending the [`GET /platform/v1/customers/{customerReference}`](../../api-explorer/endpoints/get-customer-with-external-status) request. * Portal: Track progress and statuses directly through the BVNK Portal. * Webhooks: Set up webhooks to receive automated status change notifications. --- ## Retrieve customer details With these endpoints, you can retrieve information about a specific customer within the system: key customer data, including their profile, associated wallets, and other relevant details. You can list all customers or acquire details of a specific one. *** ## Retrieve all customers To get a list of all your customers in the system, send the [`GET platform/v1/customers`](../../../api-explorer/endpoints/get-customer-with-external-status) request. In the response, you will receive the complete list of customers with the details: ```json Response { "content": [ { "reference": "8a0e1da2-2af3-4b9e-a221-e74db4e01b06", "status": "VERIFIED", "type": "COMPANY", "model": "EMBEDDED", "name": "LB Ltd.", "description": "Embedded customer" }, { "reference": "e38af699-5595-4ec4-a4d3-89b85e4fc906", "status": "PENDING", "type": "COMPANY", "model": "EMBEDDED", "name": "The Walt Disney company", "description": "Embedded customer two" } ], "pageable": { "pageNumber": 0, "pageSize": 2, "sort": { "empty": true, "sorted": false, "unsorted": true }, "offset": 0, "paged": true, "unpaged": false }, "last": false, "totalPages": 42, "totalElements": 83, "first": true, "size": 2, "number": 0, "sort": { "empty": true, "sorted": false, "unsorted": true }, "numberOfElements": 2, "empty": false } ``` *** ## Retrieve customer's details To get the detailed information of a specific customer, send the [`GET /platform/v1/customers/{customerReference}`](../../../api-explorer/endpoints/get-customer-with-external-status) with the `{customerReference}` specified in the path. In the successful response, you receive detailed information on this customer: ```json Response { "reference": "8a0e1da2-2af3-4b9e-a221-e74db4e01b06", "status": "PENDING", "type": "COMPANY", "company": { "name": "LB Ltd.", "description": "Embedded customer", "taxResidenceCountryCode": "US", "registrationNumber": "21-4532998", "address": { "addressLine1": "101 West Broadway", "addressLine2": "Suite 1975", "city": "San Diego", "postalCode": "92101", "countryCode": "US", "country": "United States" }, "associates": [ { "person": { "reference": "911685b0-acaa-418d-bd91-ed80b96c62cf", "firstName": "Sean", "lastName": "Bond", "dateOfBirth": "2001-01-31", "address": { "addressLine1": "101 West Broadway", "addressLine2": "Suite 1975", "city": "San Diego", "postalCode": "92101", "countryCode": "US", "country": "United States" } }, "details": { "birthCountryCode": "US", "contactInfo": { "emailAddress": "Sean.Bond@disney.com", "phoneNumber": "+123456789" }, "documentNumber": "123456789", "documentInfo": { "number": "123456789", "type": "DRIVERS_LICENSE", "issuingCountryCode": "US" }, "taxIdentification": { "number": "914764628", "taxResidenceCountryCode": "US" } }, "title": "CEO", "riskScore": "LOW", "controls": { "hasOwnership": true, "hasControl": true, "isSigner": false }, "verification": { "url": "https://verify.com/2", "status": "infoRequired" } }, { "person": { "reference": "c4cfa062-ea5f-49ca-96e0-8495e648520e", "firstName": "Luke", "lastName": "Dickens", "dateOfBirth": "2001-01-31", "address": { "addressLine1": "101 West Broadway", "addressLine2": "Suite 1975", "city": "San Diego", "postalCode": "92101", "countryCode": "US", "country": "United States" } }, "details": { "birthCountryCode": "US", "contactInfo": { "emailAddress": "Luke.Dickens@disney.com", "phoneNumber": "+123456789" }, "documentNumber": "123456789", "documentInfo": { "number": "123456789", "type": "DRIVERS_LICENSE", "issuingCountryCode": "US" }, "taxIdentification": { "number": "914764628", "taxResidenceCountryCode": "US" } }, "title": "Representative", "riskScore": "LOW", "controls": { "hasOwnership": false, "hasControl": false, "isSigner": true }, "verification": { "url": "https://verify.com/3", "status": "infoRequired" } } ], "verification": { "url": "https://verify.com/1", "status": "infoRequired" } }, "riskScore": "LOW" } ``` *** ## List required information When receiving the response with the status `INFO_REQUIRED` from [`GET /platform/v1/customers/{customerReference}`](../../../api-explorer/endpoints/get-customer-with-external-status), you can request the complete list of missing [documents required](../add-documents-to-customers) to complete the customer onboarding. For that, send the [`GET /platform/v1/customers/{customerReference}/info-required` ](../../../api-explorer/endpoints/get-customer-info-required) request with the ID of your customer in the path. The customer can be in the following statuses: * `INFO_REQUIRED`: Additional documents or information are required to complete the customer onboarding. * `REJECTED`: The customer application was rejected and the onboarding cannot proceed. * `VERIFIED`: The customer application is approved and the onboarding is complete. In this case, a similar corresponding response is shown: ```json { "status": "VERIFIED" } ``` For the status `INFO_REQUIRED`, you receive the following responses, depending on the customer type: In the response, you receive the [list of documents](../../compliance-requirements/embedded-compliance-requirements-businesses) to provide. ```json Example Response with the List of Required Documents { "status": "INFO_REQUIRED", "infoRequired": { "questionnaires": [], "documents": [ { "docSetType": "IDENTITY", "types": [ "PASSPORT", "DRIVERS", "ID_CARD", "RESIDENCE_PERMIT" ] }, { "docSetType": "SELFIE", "types": [ "SELFIE" ] } ], "data": [] } } ``` According to the earlier example, you must upload the following documents for your customer: * Any `IDENTITY`, for example, a passport or a residence permit. * `SELFIE` In the response, you receive the [list of documents](../../compliance-requirements/embedded-compliance-requirements-businesses) to provide. ```json { "status": "INFO_REQUIRED", "infoRequired": { "questionnaires": [ "QuestionnaireFull" ] } } ``` For example, the earlier response shows that you need to provide a questionnaire. To upload it, follow the [Gather Compliance Data for Onboarding](../onboard-epc) guides. After you pass the "questionnaire" stage, you could receive another response requiring you to provide the `associates` information. ```json { "status": "INFO_REQUIRED", "infoRequired": { "associates": [ { "roles": [ "BUSINESS_OWNER", "DIRECTOR", "ACCOUNT_REPRESENTATIVE" ] } ] } } ``` After you add the information, you get the following response: ```json { "status": "INFO_REQUIRED", "infoRequired": { "questionnaires": [ "additionalCompanyDat" ], "associates": [ { "customerPersonReference": "6599f942-dd82-4c1c-a9f1-d5e0e40a4368", "associateName": "Gabriel Syme", "roles": [ "BUSINESS_OWNER", "DIRECTOR", "ACCOUNT_REPRESENTATIVE" ], "documents": [ { "docSetType": "IDENTITY", "types": [ "DRIVERS", "ID_CARD", "RESIDENCE_PERMIT", "PASSPORT" ] }, { "docSetType": "SELFIE", "types": [ "SELFIE" ] }, { "docSetType": "PROOF_OF_RESIDENCE", "types": [ "UTILITY_BILL" ] } ], "data": [ "dateOfBirth", "details.nationality", "details.contactInfo.emailAddress", "details.taxIdentification.number", "details.taxIdentification.taxResidenceCountry" ] } ] } } ``` Here, the systems requests additional `data` and the complete set of documents to be uploaded. To provide it, follow the [Add Documents to customers](../add-documents-to-customers) guides to upload or re-upload the documents. If the documents still can't be accepted after re-upload, you can send the link from `verification.url` to your customers so that they can provide the correct materials. ```json "verification": { "status": "infoRequired", "url": "https://VERIFICATION_WEBSITE.com/C6PqOEXyQRFtSrDz" } ``` Let's say, you've provided the needed information and documents, but the selfie was of poor quality. In this case, you receive the following response: ```json { "status": "INFO_REQUIRED", "infoRequired": { "questionnaires": [ "additionalCompanyDat" ], "associates": [ { "customerPersonReference": "6599f942-dd82-4c1c-a9f1-d5e0e40a4368", "associateName": "Gabriel Syme", "roles": [ "BUSINESS_OWNER", "DIRECTOR", "ACCOUNT_REPRESENTATIVE" ], "documents": [ { "docSetType": "SELFIE", "types": [ "SELFIE" ] } ] } ] } } ``` After all the requirements are satisfied, the response may look like as follows: ```json { "status": "INFO_REQUIRED", "infoRequired": { } } ``` ## Retrieve customer details by page and size To get the detailed information of a specific customer, send the [`GET /platform/v1/customers?page={page}&size={size}`](../../../api-explorer/endpoints/get-customer-with-external-status) with the `{page}` and `{size}` specified in the path. ```json Request curl --location 'https://api.sandbox.bvnk.com/platform/v1/customers?page=0&size=20' \ --header 'Authorization: Hawk id="vbfc61D890wg6LAAVbkR11qP9O6cXeMNmKWgcUNZaOHPiQeebp9cl6h02tWv84R8", ts="1728656129", nonce="nt0eD9", mac="gv77Nu/nVWEQo8sfp/leXrC8xVs6UlOG+AoxZlyltJw="' ``` In the successful response, you will receive the details of the customers: ```json Response { "content": [ { "reference": "61461ad5-0599-4a4a-8e4e-c2575605f5bb", "status": "PENDING", "type": "COMPANY", "name": "UK Ltd Version 2.0", "description": "Number 1 customer, be super nice." }, { "reference": "60bf3fed-7e76-4cc8-9c1f-e0f19878e0c8", "status": "PENDING", "type": "COMPANY", "name": "UK Ltd Version 2.0", "description": "Number 1 customer, be super nice." } ], "pageable": { "pageNumber": 0, "pageSize": 2, "sort": { "empty": true, "sorted": false, "unsorted": true }, "offset": 0, "paged": true, "unpaged": false }, "last": false, "totalPages": 5, "totalElements": 9, "first": true, "size": 2, "number": 0, "sort": { "empty": true, "sorted": false, "unsorted": true }, "numberOfElements": 2, "empty": false } ``` | Attribute | Type | Description | | ------------------ | ------- | ------------------------------------------------------------------------ | | `pageNumber` | Integer | Current page number (e.g., 0 for the first page). | | `pageSize` | Integer | Number of records per page (e.g., 2). | | `sort.empty` | Boolean | Whether the sort criteria is empty (true means no sorting). | | `sort.sorted` | Boolean | Whether the results are sorted (false means no sorting). | | `sort.unsorted` | Boolean | Whether the results are unsorted (true means not sorted). | | `offset` | Integer | Offset of the first record (e.g., 0, meaning no offset). | | `paged` | Boolean | Whether the results are paginated (true). | | `unpaged` | Boolean | Whether the results are not paginated (false). | | `last` | Boolean | Whether this is the last page of results (false means more pages exist). | | `totalPages` | Integer | Total number of pages (e.g., 10 pages). | | `totalElements` | Integer | Total number of elements across all pages (e.g., 20 elements). | | `first` | Boolean | Whether this is the first page (true). | | `size` | Integer | Number of elements per page (e.g., 2). | | `number` | Integer | The current page number (e.g., 0 for the first page). | | `sort.empty` | Boolean | Whether the sort criteria is empty (true). | | `sort.sorted` | Boolean | Whether the results are sorted (false). | | `sort.unsorted` | Boolean | Whether the results are unsorted (true). | | `numberOfElements` | Integer | Number of elements in the current page (e.g., 2). | | `empty` | Boolean | Whether the page is empty (false means there are elements). | --- ## Prepare customer agreements Before [creating customer accounts via API](../creating-an-embedded-customer-company-api), ensure your customers have accepted and signed BVNK's Terms and Conditions. The agreements can be signed in the following ways: * Via [HostedURL](#sign-agreements-via-hostedurl) * Via [API](#sign-agreements-via-api) ## Sign agreements via HostedURL :::info We recommend preparing the Agreement Page before you set up the signing procedure. The page's URL will later be used as `{redirect URL}`. ::: To sign an agreement using a **HostedURL with a redirect**: 1. Send a [`POST /customers/agreement/sessions`](../../../api-explorer/endpoints/create-agreement-session) request with the following JSON payload to create a unique Hosted Agreement signing session. ```json Example Payload { "customerType": "INDIVIDUAL", "countryCode": "US", "useCase": "STABLECOIN_PAYOUTS" } ``` The response from BVNK includes the agreement details: ```json Example Response { "reference": "79d697b7-489a-4a01-96a2-f7403e8c5735", "status": "PENDING", "expiresOn": "2025-12-12T14:15:10Z", "agreements": [...]" } ``` The `reference` field is the unique customer agreement session identifier. 2. Construct the redirect link to BVNK's Agreement Page by adding `session={reference}` and `redirectUri={redirect URL}` as query parameters to the base URL. | Environment | Example of the constructed redirect link | | :---------- | :--------------------------------------- | | Sandbox | [`https://signup.sandbox.bvnk.com/agreements?session={reference}&redirectUri=http://example.com/callback`](https://signup.sandbox.bvnk.com/agreements?session={reference}&redirectUri=http://example.com/callback) | | Production | [`https://signup.bvnk.com/agreements?session={reference}&redirectUri=http://example.com/callback`](https://signup.bvnk.com/agreements?session={reference}&redirectUri=http://example.com/callback) | 3. Redirect your customer to the constructed BVNK Agreement Page. ### What happens next * The user reviews the required agreements on the Agreement Page and can choose to **Submit** or **Cancel**. * When the user clicks **Submit**, the session status updates to `SIGNED`, completing the signature process. * The user is redirected via `302` to the provided `redirectUri`, with the session status (`SIGNED` or `DECLINED`) included as a query parameter. Once the agreement status changes to `SIGNED`, send a [`POST /platform/v1/customers`](../../../api-explorer/endpoints/create-customer) request with the included `signedAgreementSessionReference` from the previous step to create the Embedded customer. For the detailed instructions, see [Create a customer via API](../creating-an-embedded-customer-company-api). ## Sign agreements via API BVNK supports a direct API method for signing agreements, eliminating the need for user redirection. To create an Agreement Session via API, send a [`POST /customers/agreement/sessions`](../../../api-explorer/endpoints/create-agreement-session) request with the following body parameters: ```json Example body parameters { "countryCode": "DE", "customerType": "INDIVIDUAL", "useCase": "STABLECOIN_PAYOUTS" } ``` | Parameter | Description | Required | | :--- | :--- | :--- | | `countryCode` | Country code of the customer's address in the ISO 3166-1 alpha-2 format, for example, `"US"`. | :white_check_mark: | | `customerType` | Type of customer. Available options: - `INDIVIDUAL`: A natural person.- `COMPANY`: A legal entity. | :white_check_mark: | | `useCase` | Type of product to which the customer agreement applies. Available options:- `STABLECOIN_PAYOUTS`: Enables sending stablecoin payments to customers' crypto wallets from fiat or crypto balances.- `EMBEDDED_STABLECOIN_WALLETS`: Allows partners to create and manage stablecoin wallets for their customers to facilitate crypto transactions.- `EMBEDDED_FIAT_ACCOUNTS`: Enables creating virtual accounts with unique vIBANs for customers to send, hold, and receive fiat funds. | :white_check_mark: | The response includes the agreement session details. ```json Example Response { "reference": "5f74fee5-3e8e-4dcc-85cd-24b9205bb44d", "status": "PENDING", "customerType": "INDIVIDUAL", "useCase": "STABLECOIN_PAYOUTS", "countryCode": "DE", "expiresOn": "2025-05-23T15:05:20.859141Z", "agreements": [ ... ] } ``` | Parameter | Description | | :--- | :--- | | `reference` | Unique session identifier. | | `status` | Session status. Available options:- `PENDING`: The session is pending and the agreements have not been signed yet.- `SIGNED`: The agreement has been signed by the customer.- `DECLINED`: The customer has declined the agreement. | | `customerType` | Type of customer. Available options:- `INDIVIDUAL`: A natural person.- `COMPANY`: A legal entity. | | `useCase` | Type of product to which the customer agreement applies. Available options:- `STABLECOIN_PAYOUTS`: Enables sending stablecoin payments to customers' crypto wallets from fiat or crypto balances.- `EMBEDDED_STABLECOIN_WALLETS`: Allows partners to create and manage stablecoin wallets for their customers to facilitate crypto transactions.- `EMBEDDED_FIAT_ACCOUNTS`: Enables creating virtual accounts with unique vIBANs for customers to send, hold, and receive fiat funds. | | `countryCode` |Country code of the customer's address in the ISO 3166-1 alpha-2 format, for example, `"US"`. | | `expiresOn` | Session expiration (ISO8601 timestamp). | | `agreements` | List of agreements for review and acceptance. | Each agreement includes: * `name`: Agreement identifier. * `displayName`: Public-facing title. * `description`: Agreement details. * `url`: URL for agreement document. * `status`: Session status of the agreement signing. * `privacyPolicyName`: Privacy policy title. * `privacyPolicyDescription`: Privacy policy details. * `privacyPolicyUrl`: URL to privacy policy. ## Update agreement session status To update the agreement session status, follow these steps: 1. Send [`PUT /customers/agreement/sessions/{reference}`](../../../api-explorer/endpoints/update-agreement-session) request with the session reference obtained in the previous step. 2. In the request, include the following payload: * session status (`SIGNED` or `DECLINED`) * IP address ```json Example Request { "status": "SIGNED", "ipAddress": "192.172.1.16" } ``` The session status will update accordingly. ## Verify session status To retrieve the session status, send the [`GET /customers/agreement/sessions/{reference}`](../../../api-explorer/endpoints/get-agreement-session-status) request with the session reference. The response confirms the current session status (`SIGNED`, `PENDING`, or `DECLINED`). ```json Example Verified Response { "reference": "5f74fee5-3e8e-4dcc-85cd-24b9205bb44d", "status": "SIGNED", ... } ``` ## Key considerations * Store session references securely. * Clearly inform your customers about the legally binding nature of agreements. * Monitor and manage session expirations carefully. :::important The **Customers** section in Portal and API is not enabled by default when a sandbox account is created. Contact the Solutions team to configure this capability. ::: --- **What's next?** - [Create a customer](../creating-an-embedded-customer-company-api) --- ## Update customer details With these endpoints, you can update information about a specific customer within the system: key customer data, including their profile, associated wallets, and other relevant details. You can update the customer's profile, address, and other relevant details. *** ## Update customer details To update the detailed information of a specific customer, send the `PUT /platform/v1/customers/{customerReference}` with the `{customerReference}` specified in the path. In the request body, you can update the following fields: ```json Request { "name": "Updated Company Name", "description": "Updated description", "address": { "addressLine1": "Updated Address Line 1", "addressLine2": "Updated Address Line 2", "city": "Updated City", "postalCode": "Updated Postal Code", "countryCode": "US", "country": "United States" } } ``` In the successful response, you receive the updated customer information: ```json Response { "reference": "8a0e1da2-2af3-4b9e-a221-e74db4e01b06", "status": "PENDING", "type": "COMPANY", "company": { "name": "Updated Company Name", "description": "Updated description", "taxResidenceCountryCode": "US", "registrationNumber": "21-4532998", "address": { "addressLine1": "Updated Address Line 1", "addressLine2": "Updated Address Line 2", "city": "Updated City", "postalCode": "Updated Postal Code", "countryCode": "US", "country": "United States" }, "associates": [ { "person": { "reference": "911685b0-acaa-418d-bd91-ed80b96c62cf", "firstName": "Sean", "lastName": "Bond", "dateOfBirth": "2001-01-31", "address": { "addressLine1": "101 West Broadway", "addressLine2": "Suite 1975", "city": "San Diego", "postalCode": "92101", "countryCode": "US", "country": "United States" } }, "details": { "birthCountryCode": "US", "contactInfo": { "emailAddress": "Sean.Bond@disney.com", "phoneNumber": "+123456789" }, "documentNumber": "123456789", "documentInfo": { "number": "123456789", "type": "DRIVERS_LICENSE", "issuingCountryCode": "US" }, "taxIdentification": { "number": "914764628", "taxResidenceCountryCode": "US" } }, "title": "CEO", "riskScore": "LOW", "controls": { "hasOwnership": true, "hasControl": true, "isSigner": false }, "verification": { "url": "https://verify.com/2", "status": "infoRequired" } } ], "verification": { "url": "https://verify.com/1", "status": "infoRequired" } }, "riskScore": "LOW" } ``` | Attribute | Type | Description | | :--- | :--- | :--- | | name | String | Updated name of the company. | | description | String | Updated description of the company. | | address | Object | Updated address of the company. | | address.addressLine1 | String | Updated primary address line. | | address.addressLine2 | String | Updated secondary address line. | | address.city | String | Updated city. | | address.postalCode | String | Updated postal code. | | address.countryCode | String | Updated country code. | | address.country | String | Updated full country name. | *** ## Update customer status To update the status of a specific customer, send the `PUT /platform/v1/customers/{customerReference}/status` with the `{customerReference}` specified in the path. In the request body, you can update the status: ```json Request { "status": "VERIFIED" } ``` In the successful response, you receive the updated customer status: ```json Response { "reference": "8a0e1da2-2af3-4b9e-a221-e74db4e01b06", "status": "VERIFIED", "type": "COMPANY", "company": { "name": "Updated Company Name", "description": "Updated description", "taxResidenceCountryCode": "US", "registrationNumber": "21-4532998", "address": { "addressLine1": "Updated Address Line 1", "addressLine2": "Updated Address Line 2", "city": "Updated City", "postalCode": "Updated Postal Code", "countryCode": "US", "country": "United States" }, "associates": [ { "person": { "reference": "911685b0-acaa-418d-bd91-ed80b96c62cf", "firstName": "Sean", "lastName": "Bond", "dateOfBirth": "2001-01-31", "address": { "addressLine1": "101 West Broadway", "addressLine2": "Suite 1975", "city": "San Diego", "postalCode": "92101", "countryCode": "US", "country": "United States" } }, "details": { "birthCountryCode": "US", "contactInfo": { "emailAddress": "Sean.Bond@disney.com", "phoneNumber": "+123456789" }, "documentNumber": "123456789", "documentInfo": { "number": "123456789", "type": "DRIVERS_LICENSE", "issuingCountryCode": "US" }, "taxIdentification": { "number": "914764628", "taxResidenceCountryCode": "US" } }, "title": "CEO", "riskScore": "LOW", "controls": { "hasOwnership": true, "hasControl": true, "isSigner": false }, "verification": { "url": "https://verify.com/2", "status": "infoRequired" } } ], "verification": { "url": "https://verify.com/1", "status": "infoRequired" } }, "riskScore": "LOW" } ``` | Attribute | Type | Description | | :--- | :--- | :--- | | status | String | Updated status of the customer (e.g., "VERIFIED"). Possible statuses could include PENDING, VERIFIED, REJECTED. | *** ## Update customer associates To update the associates of a specific customer, send the `PUT /platform/v1/customers/{customerReference}/associates` with the `{customerReference}` specified in the path. In the request body, you can update the associates: ```json Request { "associates": [ { "person": { "firstName": "Updated First Name", "lastName": "Updated Last Name", "dateOfBirth": "2001-01-31", "address": { "addressLine1": "Updated Address Line 1", "addressLine2": "Updated Address Line 2", "city": "Updated City", "postalCode": "Updated Postal Code", "countryCode": "US", "country": "United States" } }, "details": { "birthCountryCode": "US", "contactInfo": { "emailAddress": "updated.email@example.com", "phoneNumber": "+123456789" }, "documentNumber": "123456789", "documentInfo": { "number": "123456789", "type": "DRIVERS_LICENSE", "issuingCountryCode": "US" }, "taxIdentification": { "number": "914764628", "taxResidenceCountryCode": "US" } }, "title": "Updated Title", "riskScore": "LOW", "controls": { "hasOwnership": true, "hasControl": true, "isSigner": false } } ] } ``` In the successful response, you receive the updated customer associates: ```json Response { "reference": "8a0e1da2-2af3-4b9e-a221-e74db4e01b06", "status": "VERIFIED", "type": "COMPANY", "company": { "name": "Updated Company Name", "description": "Updated description", "taxResidenceCountryCode": "US", "registrationNumber": "21-4532998", "address": { "addressLine1": "Updated Address Line 1", "addressLine2": "Updated Address Line 2", "city": "Updated City", "postalCode": "Updated Postal Code", "countryCode": "US", "country": "United States" }, "associates": [ { "person": { "reference": "911685b0-acaa-418d-bd91-ed80b96c62cf", "firstName": "Updated First Name", "lastName": "Updated Last Name", "dateOfBirth": "2001-01-31", "address": { "addressLine1": "Updated Address Line 1", "addressLine2": "Updated Address Line 2", "city": "Updated City", "postalCode": "Updated Postal Code", "countryCode": "US", "country": "United States" } }, "details": { "birthCountryCode": "US", "contactInfo": { "emailAddress": "updated.email@example.com", "phoneNumber": "+123456789" }, "documentNumber": "123456789", "documentInfo": { "number": "123456789", "type": "DRIVERS_LICENSE", "issuingCountryCode": "US" }, "taxIdentification": { "number": "914764628", "taxResidenceCountryCode": "US" } }, "title": "Updated Title", "riskScore": "LOW", "controls": { "hasOwnership": true, "hasControl": true, "isSigner": false }, "verification": { "url": "https://verify.com/2", "status": "infoRequired" } } ], "verification": { "url": "https://verify.com/1", "status": "infoRequired" } }, "riskScore": "LOW" } ``` | Attribute | Type | Description | | :--- | :--- | :--- | | associates | Array | Updated list of associates for the company. | | associates.person.firstName | String | Updated first name of the associate. | | associates.person.lastName | String | Updated last name of the associate. | | associates.person.dateOfBirth | String | Updated date of birth of the associate. | | associates.person.address | Object | Updated address of the associate. | | associates.details.contactInfo.emailAddress | String | Updated email address of the associate. | | associates.details.contactInfo.phoneNumber | String | Updated phone number of the associate. | | associates.title | String | Updated title of the associate. | | associates.riskScore | String | Updated risk score of the associate. | | associates.controls.hasOwnership | Boolean | Updated ownership status of the associate. | | associates.controls.hasControl | Boolean | Updated control status of the associate. | | associates.controls.isSigner | Boolean | Updated signer status of the associate. | --- ## Create a sandbox account Before you can start building with BVNK's APIs, you'll need to create a sandbox account. The sandbox environment allows you to test your integration without using real funds or affecting production systems. ## Create your sandbox account 1. Go to [https://signup.sandbox.bvnk.com/create-dev-account](https://signup.sandbox.bvnk.com/create-dev-account). 2. Fill in the registration form with your details: - **Name**: Enter your first and last name. - **Work email**: Enter your primary email for account access. Addresses from public domains (like Gmail, Outlook, Yahoo, or Hotmail) are not permitted. - **Password**: Create a strong password for your account. - **Company name**: Enter your organization or company name. 3. Click **Sign Up** to proceed. ![BVNK Sandbox Signup Form](/img/bvnk/get-started/sign-up-sandbox.png) 4. Check your email inbox for a verification message from BVNK. If you don't see the email, check your spam or junk folder. 5. Copy the six-digit code from the email and enter it in the verification code field. ![Email Verification](/img/bvnk/get-started/confirm-email.png) Click **Continue** to complete the verification process. You'll be redirected to the sandbox portal dashboard. Once verified, you'll have access to your sandbox dashboard. There, you can complete any additional company details if prompted. Your account is now ready for [API key generation](../generate-api-keys). --- **Ready to start building?** Once your sandbox account is set up, you can: - [Generate your API credentials](../generate-api-keys). - [Create your first test wallet](../creating-your-first-wallet). - [Set up webhooks](../create-webhook-listener). --- ## 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). ## 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. * **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). ## 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. You can specify your Webhook URL during the creation of a Merchant. To add or change the webhook URL for an existing Merchant, go to the Merchant menu, click **Edit**, and enter the required address. After you click **Save**, the Webhook URL will be added along with the generated Secret key. Click the Merchant's name to see the changes: ![](/img/bvnk/get-started/merchant-changes.png) 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. To calculate the signature, do the following: 1. Get the payload of the webhook. 2. Obtain the secret key: go to **Manage Account** > **Manage Merchants**, and click a Merchant Name to open its details. ![](/img/bvnk/get-started/secret-key-management.png) 3. Generate the signature using the Secret Key and payload components. See the examples for different languages below. :::note Some legacy webhooks may still require concatenation of the following items to create a signature: * Webhook URL * `content-type` * Webhook payload Note that these webhooks will be soon discontinued. ::: ```java 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. """ 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 BVNK offers capabilities to create and manage crypto wallets directly from within the BVNK portal. :::warning 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 } } ``` | Parameter | Description | | :----------------------------------- | :-------------------------------------------------------------------------------- | | `currencyCode` | Currency code for the wallet. | | `name` | Name of the wallet. | | `customerReference` | Unique reference to the customer who owns the wallet. | | `instruction` | Information on the wallet type. | | `instruction.type` | Type of the wallet: fiat or crypto. | | `instruction.walletProfile` | Profile to assign to the virtual account. | | `instruction.virtualAccountRequired` | Indication whether a virtual account is required. Currently supports `true` only. | 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 using the following query parameters. | Query Parameters | Values | | :--- | :--- | | `currencyCodes` | Code for fiat and cryptocurrency. See the detailed [list of supported currencies](/bvnk/references/currencies). | | `paymentMethods` | Payment methods available for the wallet. See the detailed [list of payment methods](/bvnk/references/payment-methods). | 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. In the response, you receive the detailed information on each wallet:
Response ```json Response { "content": [ { "id": "a:25061762277338:4nJVWl1:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": "7e38e85f-8e1b-486d-b24f-28e4192f57f6", "name": "Jonsal", "status": "ACTIVE", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [ { "type": "FIAT", "accountHolderName": "Embedded customer A", "accountNumber": "900768107004", "code": "101019644", "accountNumberFormat": "ABA", "paymentReference": null } ] }, { "id": "a:25061762217894:7kqBQY6:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": "9ba4c65c-8c81-4e39-80b6-a803d9e66efd", "name": "John Legend", "status": "INACTIVE", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [] }, { "id": "a:25061762124495:Sichkow:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": "9ba4c65c-8c81-4e39-80b6-a803d9e66efd", "name": "USD", "status": "INACTIVE", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [] }, { "id": "a:25060450775603:v28lmzW:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": null, "name": "Dave's Wallet", "status": "ACTIVE", "balance": { "value": 0.00, "currencyCode": "EUR" }, "ledgers": [] }, { "id": "a:25052770895915:fQcLGMh:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": "7e38e85f-8e1b-486d-b24f-28e4192f57f6", "name": "JL Wallet", "status": "ACTIVE", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [ { "type": "FIAT", "accountHolderName": "Embedded customer A", "accountNumber": "900679904231", "code": "101019644", "accountNumberFormat": "ABA", "paymentReference": null } ] }, { "id": "a:25052256822125:xAWTzSB:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": null, "name": "USDC Wallet", "status": "ACTIVE", "balance": { "value": 1106.71, "currencyCode": "USDC" }, "ledgers": [] }, { "id": "a:25052233445967:xnrik2v:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": null, "name": "USDT Wallet", "status": "ACTIVE", "balance": { "value": 120.50, "currencyCode": "USDT" }, "ledgers": [] }, { "id": "a:25052137356562:qjOSsJf:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": "7e38e85f-8e1b-486d-b24f-28e4192f57f6", "name": "USD Wallet NEW", "status": "TERMINATED", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [ { "type": "FIAT", "accountHolderName": "Embedded customer A", "accountNumber": "900072464417", "code": "101019644", "accountNumberFormat": "ABA", "paymentReference": null } ] }, { "id": "a:25051268495394:hNyZppE:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": null, "name": "Bitcoin Wallet Test account # 1", "status": "INACTIVE", "balance": { "value": 0.00, "currencyCode": "BTC" }, "ledgers": [] }, { "id": "a:25042472328513:DjpwMfa:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": "7e38e85f-8e1b-486d-b24f-28e4192f57f6", "name": "USD Wallet Test", "status": "ACTIVE", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [ { "type": "FIAT", "accountHolderName": "Embedded customer A", "accountNumber": "900222018455", "code": "101019644", "accountNumberFormat": "ABA", "paymentReference": null } ] }, { "id": "a:25042460023997:xgImmdY:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": null, "name": "ETH Wallet", "status": "INACTIVE", "balance": { "value": 0.23, "currencyCode": "ETH" }, "ledgers": [] }, { "id": "a:25041166078335:oYJeFUK:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": "7e38e85f-8e1b-486d-b24f-28e4192f57f6", "name": "USDT Wallet", "status": "ACTIVE", "balance": { "value": 71.63, "currencyCode": "USDT" }, "ledgers": [] }, { "id": "a:25040249187073:wR42W6d:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": null, "name": "USDC Wallet", "status": "INACTIVE", "balance": { "value": 200.00, "currencyCode": "USDC" }, "ledgers": [] }, { "id": "a:25040150970798:mGX7Twm:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": null, "name": "Wallet EUR", "status": "INACTIVE", "balance": { "value": 0.00, "currencyCode": "EUR" }, "ledgers": [] }, { "id": "a:25031066969227:zFSl3Ea:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": "7e38e85f-8e1b-486d-b24f-28e4192f57f6", "name": "USD Wallet 3", "status": "ACTIVE", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [ { "type": "FIAT", "accountHolderName": "Embedded Customer A", "accountNumber": "900554317794", "code": "101019644", "accountNumberFormat": "ABA", "paymentReference": null } ] }, { "id": "a:25031066539770:Ia9mdf9:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": "9ba4c65c-8c81-4e39-80b6-a803d9e66efd", "name": "USD Wallet Customer B", "status": "INACTIVE", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [] }, { "id": "a:25030652262360:ewsQxR8:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": "7e38e85f-8e1b-486d-b24f-28e4192f57f6", "name": "USD Wallet 2", "status": "ACTIVE", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [ { "type": "FIAT", "accountHolderName": "Embedded customer A", "accountNumber": "900914731533", "code": "101019644", "accountNumberFormat": "ABA", "paymentReference": null } ] }, { "id": "a:25030559783181:81aaB4K:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": null, "name": "USD Swift", "status": "ACTIVE", "balance": { "value": 137.00, "currencyCode": "USD" }, "ledgers": [ { "type": "FIAT", "accountHolderName": "System Pay Services Solutions Spain S.L.", "accountNumber": "ES250182851098200007418", "code": "BBVAESMMXXX", "accountNumberFormat": "SWIFT", "paymentReference": "REF-CL1CDC7" } ] }, { "id": "a:25030558827793:LO8IHmy:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": "7e38e85f-8e1b-486d-b24f-28e4192f57f6", "name": "USD Embedded Wallet", "status": "ACTIVE", "balance": { "value": 872.00, "currencyCode": "USD" }, "ledgers": [ { "type": "FIAT", "accountHolderName": "Embedded customer A", "accountNumber": "900012647634", "code": "101019644", "accountNumberFormat": "ABA", "paymentReference": null } ] }, { "id": "a:25030548796673:JtdGolN:1", "accountReference": "f539c983-c8cd-4db0-98bb-2de9e79d0e64", "customerReference": null, "name": "USD Main Wallet", "status": "INACTIVE", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [ { "type": "FIAT", "accountHolderName": "Commercial US", "accountNumber": "900364186010", "code": "101019644", "accountNumberFormat": "ABA", "paymentReference": null } ] } ], "pageable": { "pageNumber": 0, "pageSize": 20, "sort": [], "offset": 0, "paged": true, "unpaged": false }, "last": false, "totalElements": 23, "totalPages": 2, "first": true, "size": 20, "number": 0, "sort": [], "numberOfElements": 20, "empty": false } ```
### 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}` specified in the path. In the response, you receive the list with detailed information on each customer wallet:
Response ```json Response { "content": [ { "id": "a:24101151618956:Xlfn0oR:1", "accountReference": "ceb9400d-eee2-4cc0-89dc-1b3548f7291d", "customerReference": "a7e21c62-27b8-4b3b-b51e-eb10edeb1731", "name": "My USD Wallet", "status": "INACTIVE", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [] }, { "id": "a:24101151422432:gCl7SeI:1", "accountReference": "ceb9400d-eee2-4cc0-89dc-1b3548f7291d", "customerReference": "a7e21c62-27b8-4b3b-b51e-eb10edeb1731", "name": "My USD Wallet 2", "status": "INACTIVE", "balance": { "value": 0.00, "currencyCode": "USD" }, "ledgers": [] } ], "pageable": { "pageNumber": 0, "pageSize": 20, "sort": [], "offset": 0, "paged": true, "unpaged": false }, "last": true, "totalElements": 2, "totalPages": 1, "first": true, "size": 20, "number": 0, "sort": [], "numberOfElements": 2, "empty": false } ```
### 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. In the response, you receive the detailed information on the wallet:
Response ```json Response { "id": "a:24101151422432:gCl7SeI:1", "accountReference": "ceb9400d-eee2-4cc0-89dc-1b3548f7291d", "customerReference": "a7e21c62-27b8-4b3b-b51e-eb10edeb1731", "name": "My USD Wallet 2", "status": "INACTIVE", "balance": { "value": 1000.00, "currencyCode": "USD" }, "ledgers": [] } ```
## 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 explains the different delivery models available for BVNK. Not sure which model is right for you? Take our interactive quiz to find out: The direct model is the most basic delivery model. You contract directly with BVNK and access our payment capabilities through the BVNK portal or API for your own use. You have full control over your payment flow and can customize it to your needs. ![](/img/bvnk/get-started/direct-model.png) **Who is it for:** Companies looking to send, receive, or hold stablecoins to and from 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.mdx) - [Receive payments](../use-cases/stablecoin-payments-for-platforms/get-payment-overview.mdx) - [Convert funds (on-ramp)](../use-cases/on-off-ramp/on-ramp-overview.mdx) - [Receive, send, and store funds with Virtual Accounts](../use-cases/virtual-accounts/va-overview.mdx) 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) **Who is it for:** Larger enterprises (platforms, or marketplaces) that want to embed stablecoins directly into their customer-facing ecosystem. **Flow of funds:** You move funds on behalf of your customers or they 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 the partners platform. **Use cases**: - [Send stablecoin payments](../use-cases/stablecoin-payments-for-platforms/payout-overview.mdx) - [Receive payments](../use-cases/stablecoin-payments-for-platforms/get-payment-overview.mdx) - [Send payments via customers' wallets](../use-cases/stablecoin-payments-for-platforms/epc-stablecoin-overview.mdx) - [Convert funds (on-ramp)](../use-cases/on-off-ramp/on-ramp-overview.mdx) --- ## 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**| **[Send stablecoin payments](../use-cases/stablecoin-payments-for-platforms/payout-overview.mdx)** | **[Receive stablecoin payments](../use-cases/stablecoin-payments-for-platforms/get-payment-overview.mdx)** | **[Send stablecoin via customers' wallets](../use-cases/stablecoin-payments-for-platforms/epc-stablecoin-overview.mdx)** | **[Convert funds (on-ramp)](../use-cases/on-off-ramp/on-ramp-overview.mdx)** | **[Receive, send, and store funds with Virtual Accounts](../use-cases/virtual-accounts/va-overview.mdx)** | |:----------:|-------------|----------|----------|----------|----------| | **Use it to** |Pay out winnings and earning to customersSend stablecoins from fiat balanceSend stablecoins as a salary to employees and contractors | Collect payments from your users in stablecoins, while you still receive fiatAllow your users to top up their account in stablecoins | Allow stablecoin payouts from your customers to their users from a pre-funded fiat balance | Allow your customers to pay out in crypto directly from your platform | Enable customers to send and receive fiat funds for crypto tradingProcess 3rd party payments with optional stablecoin pre-funding | | **For who?** | Gaming PlatformCFD / FX brokersTrading PlatformsNeobanksPayment Service Providers (PSPs) | Payment Processing NetworksPayment Service Providers | Payment Service Providers | Payment Service Providers Employer of Record servicesMarketplacesNeobanks | Payment Service ProvidersEmployer of Record servicesMarketplacesNeobanksCFD / FX brokersTrading Platforms | | **Currency flow** |fiat → cryptocrypto → crypto | crypto → fiatfiat → stablecoins | crypto → cryptofiat → stablecoins | crypto → fiatfiat → crypto | fiat → fiat | | **Delivery model** | [Direct](delivery-models.mdx?delivery-model=direct)[Embedded](delivery-models.mdx?delivery-model=custom-embedded) | [Direct](delivery-models.mdx?delivery-model=direct)[Embedded](delivery-models.mdx?delivery-model=custom-embedded) | [Embedded](delivery-models.mdx?delivery-model=custom-embedded) | [Direct](delivery-models.mdx?delivery-model=direct)[Embedded](delivery-models.mdx?delivery-model=custom-embedded) | [Direct](delivery-models.mdx?delivery-model=direct)[Embedded](delivery-models.mdx?delivery-model=custom-embedded) | | **Capabilities** |SendReceiveConvert| SendReceiveConvert |SendReceiveConvertStore| SendReceiveConvert |SendReceiveStore| --- ## 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: - [Create your first wallet](../creating-your-first-wallet) 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 test your integration in the sandbox environment, you need a [BVNK sandbox account](create-sandbox-account.mdx). 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#embedded-model). - [Create crypto wallets](creating-your-first-wallet.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. --- ## 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" } } } ``` | Parameter | Description | | :-------- | :---------- | | `walletId` | Your wallet id. | | `remittanceInformation` | Reference for the payment. | | `method` | Payment method to be utilised for the pay-in. Optional.If it is not provided, the logic will be based on other dynamically determined attributes and routed for the specific wallet.If provided, use the following methods for each currency:* GBP: `FASTER_PAYMENT` or `SWIFT`* EUR: `SEPA_CT`, `SEPA_INST`, or `SWIFT`* USD: `ACH`, `ACH_SAME_DAY`, `FEDWIRE`, `CUBIX`, or `SWIFT` | | `amount` | The amount of the pay-in transaction. | | `currency` | Currency code (ISO-4217 format) for the pay-in transaction. | | `originator.name` | Name of the sender of the pay-in. | | `originator.bankAccount` | Bank account details of the sender of the pay-in. | | `originator.bankAccount.accountNumber` | Account number of the sender of the pay-in. | | `originator.bankAccount.accountNumberFormat` | Format type of the bank account number. For example `IBAN` can be used for SWIFT and `ABA` for local USD rails.Possible values: `SCAN`, `IBAN`, `SWIFT`, `ABA`, `BBAN`, `CBIT`, `CUBIX` | | `originator.bankAccount.bankCode` | Bank code of the sender of the pay-in. | 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/use-cases/unprocessed/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`. --- ## Receive transactions report via webhook ## Generate account-level webhook and secret Create a webhook as described in [Receive webhook notifications](../receive-webhook-notifications). In the **Events** field, specify `bvnk:ledger:report:ready` to enable the receiving of reports. ![](/img/bvnk/get-started/add-webhook.png) After creating the webhook, click it in the list to open the details and copy the **Public key** value. You will later need to use it as `secretKey` to verify the webhook and calculate the signature. ![](/img/bvnk/get-started/transaction-history-report.png) ## Generate transactions report To generate a report, send the [`POST /ledger/v1/reports`](../../api-explorer/endpoints/wallet-transaction-report) request with the following payload: ```json Example Payload { "format": "csv", "type": "TRANSACTION", "from": "2025-08-01T00:00:00", "to": "2025-09-29T23:59:59", "deliveryChannel": "EMAIL", "walletId": "a:25082029387281:8DBI2gV:1" } ``` The example above triggers asynchronous transaction report generation for all of your wallets. Once the report is ready, it will be sent via webhook. Other possible parameters are: | Parameter | Required | Description | |-----------|:----------:|-------------| | `from` | :white_check_mark: | Export range from date in format 'YYYY-MM-dd'. | | `to` | :white_check_mark: | Export range to date in format 'YYYY-MM-dd'. | | `deliveryChannel` | :x: | Method of how the report will be received. Available options: "webhook", "email". Default, "email".In this scenario, provide `deliveryChannel=webhook`. | | `format` | :x: | Format of the generated file report. Available options: "CSV", "JSON". Default, JSON. | | `walletId` | :x: | Filter the transactions based on `walletId`. If omitted, all transactions will be included in the report. | | `type` | :white_check_mark: | Type of the sent report. Use `TRANSACTION`for a transactions report. | ## Handle created transaction report Webhooks for transaction reports are sent as an HTTP POST with `Content-Type: application/json` containing the following payload: ```json Example Webhook Payload { "event": "bvnk:ledger:report:ready", "eventId": "01989e35-4435-7a98-978d-780c97566ac0", "timestamp": "2025-08-12T12:15:47.7656623082", "data": { "id": "b13ae8ee-3f1c-45c4-b99c-93d784abbe02", "type": "TRANSACTION", "status": "READY", "url": "https://reports.ws.com/transaction/b13bc8ee-3f1c-45c4-b99c-93d784abbe02_a0479821-71fd-45aa-8aa3-173cvcde4e1c.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA2P6R7NUDFAGFQNAX%2F20250812%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Expires=604800&X-Amz-Signature=bf045216e6dbe2b5e1ab2664affceb0c0371ee0ced" } } ``` Verify that the event name is correct and then download the report. Note that the link to the report will remain active for the next 24 hours. After that, the report won't be accessible. --- ## Receive webhook notifications The webhook feature ensures that you are automatically informed about changes to your BVNK account and minimises the requirement for you to ask for assistance in configuring your required webhook setup. You can configure real-time notifications via webhooks for key events such as: pay-ins received, payouts sent out, refund initiated, and transaction status changed. The most significant benefit for you is the ability to automate your workflows by setting up triggers that determine which operations or activities to perform upon receiving a deposit. For example, if you run a business that issues wallets to customers and these customers make deposits to fund their wallets, you can automatically credit their wallets as soon as you receive the webhook for the successful deposit. This eliminates the need for a manual process of periodically checking the BVNK Portal to confirm deposits. ## 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**. 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. * **Events to send**: Select the events to send from the list provided. Here, you can select events for operations related to fiat and crypto. 1. Click **Add**. The new active webhook appears in the list of webhooks. Now you can do the following: * List destinations of webhook URLs. * Create a destination of webhook URLs. * Remove the destination of webhook URLs. * List events when creating a destination or viewing details. Once your payment has been processed, you will receive an **asynchronous** response (webhook) containing the details of the event to which you subscribed. This response will be sent to the Webhook URL described earlier. 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). *** ## Explore available webhooks You can subscribe to the following webhooks depending on your use case: | Event Name | Triggered when | Relevant Documentation | | :--- | :--- | :--- | | **PAYMENT WEBHOOKS** | | | | `bvnk:payment:payin:status-change` | Status of an incoming fiat payment changes | See webhook documentation | | `bvnk:payment:payout:status-change` | Status of an outgoing fiat payment changes | See webhook documentation | | `bvnk:payment:transfer:status-change` | Status of an internal payment transfer changes | [Internal Transfers](../../use-cases/create-internal-transfer) | | **CHANNEL WEBHOOKS** | | | | `bvnk:payment:channel:transaction-on-hold` | Channel transaction is placed on hold | See channel webhook documentation | | `bvnk:payment:channel:transaction-detected` | Channel transaction is detected | See channel webhook documentation | | `bvnk:payment:channel:transaction-confirmed` | Channel transaction is confirmed | See channel webhook documentation | | **CRYPTO WEBHOOKS** | | | | `bvnk:payment:crypto:refund-initiated` | Cryptocurrency payment refund is initiated | See crypto webhook documentation | | `bvnk:payment:crypto:transaction-late` | Cryptocurrency transaction is marked as late | See crypto webhook documentation | | `bvnk:payment:crypto:transaction-settled` | Cryptocurrency transaction is settled | See crypto webhook documentation | | `bvnk:payment:crypto:status-change` | Status of a cryptocurrency payment changes | See crypto webhook documentation | | `bvnk:payment:crypto:transaction-on-hold` | Cryptocurrency transaction is placed on hold | See crypto webhook documentation | | `bvnk:payment:crypto:transaction-confirmed` | Cryptocurrency transaction is confirmed | Transaction Confirmation | | `bvnk:payment:crypto:transaction-detected` | Cryptocurrency transaction is detected | Transaction Detection | | **CUSTOMER WEBHOOKS** | | | | `bvnk:platform:customer:agreement-session-status-change` | Customer agreement session status changes | [Prepare Customer Agreements](../create-embedded-partner-customer/signing-customer-agreements) | | `bvnk:platform:customer:status-change` | Customer's status changes | [Create a customer via API](../create-embedded-partner-customer/creating-an-embedded-customer-company-api) | | `bvnk:platform:customer:update` | Customer information is updated | [Create a customer via API](../create-embedded-partner-customer/creating-an-embedded-customer-company-api) | | `bvnk:platform:customer:document-status-change` | Customer document verification status changes | [Add Documents to customers](../create-embedded-partner-customer/add-documents-to-customers) | | `bvnk:platform:customer:questionnaire-status-change` | Customer questionnaire status changes | [Customer Questionnaires](../create-embedded-partner-customer/onboard-epc) | | **LEDGER WEBHOOKS** | | | | `bvnk\:ledger:\wallet:create` | New wallet is created in the ledger | See ledger webhook documentation | | `bvnk:ledger:report:ready` | Ledger report is ready for download | [Receive Transactions Report via Webhook](../receive-transactions-report-via-webhook) | *** ## 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. You can specify your Webhook URL during the creation of a Merchant. To add or change the webhook URL for an existing Merchant, go to the Merchant menu, click **Edit**, and enter the required address. After you click **Save**, the Webhook URL will be added along with the generated Secret key. Click the Merchant's name to see the changes: 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. The retry policy adds a delay before each retry, starting with a base delay and increasing the time between retries. 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. ### 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. To calculate the signature, do the following: 1. Get the payload of the webhook. 2. Obtain the secret key: go to **Manage Account** > **Manage Merchants**, and click a Merchant Name to open its details. 3. Generate the signature using the Secret Key and payload components. See the examples for different languages below. :::warning Some legacy webhooks may still require concatenation of the following items to create a signature: * Webhook URL * `content-type` * Webhook payload Note that these webhooks will be soon discontinued. ::: ```java 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 Python #!/usr/bin/env python3 """ BVNK Webhook Signature Verification - Python This script verifies HMAC-SHA256 signatures from BVNK webhook payloads. """ 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 C# 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. --- ## Add Ethereum wallet for ETH and USDT 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/use-cases/unprocessed/metamask-1-install.png) 2. Optional. Pin the extension to your browser by clicking the puzzle icon > pin icon next to MetaMask. ![](/img/bvnk/use-cases/unprocessed/metamask-2-pin.png) 3. Click the fox-head icon to open your MetaMask wallet and log in. ![](/img/bvnk/use-cases/unprocessed/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/use-cases/unprocessed/metamask-4-networks.png) 5. The **Select a network** tab opens. Click the slider to show test networks, then select **Sepolia**. ![](/img/bvnk/use-cases/unprocessed/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/use-cases/unprocessed/metamask-6-sepolia.png) *** ## Add USDT ERC20 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/use-cases/unprocessed/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/use-cases/unprocessed/metamask-8-token-contract.png) 3. When the prompt appears, click **Import**. ![](/img/bvnk/use-cases/unprocessed/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/use-cases/unprocessed/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/use-cases/unprocessed/metamask-11-complete.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/use-cases/unprocessed/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/use-cases/unprocessed/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/use-cases/unprocessed/tronlink-3-networks.png) 5. The **Networks** tab opens. Select **TRON Nile Testnet**. ![](/img/bvnk/use-cases/unprocessed/tronlink-4-testnet.png) The **TRON Nile Testnet** at the top indicates you are now on the Nile TestNet. Now you can receive funds and test transactions in the sandbox. ![](/img/bvnk/use-cases/unprocessed/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. 2. Go to [Nile Testnet](https://nileex.io/join/getJoinPage) and enter your TronLink wallet address. ![](/img/bvnk/use-cases/unprocessed/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. 2. ![](/img/bvnk/use-cases/unprocessed/tronlink-7-connect-wallet.png) 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. 3. ![](/img/bvnk/use-cases/unprocessed/tronlink-8-token-search.png) Select this token. ![](/img/bvnk/use-cases/unprocessed/tronlink-9-token-selected.png) 4. 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/use-cases/unprocessed/tronlink-10-mint.png) 5. The **Trigger Smart Contract** window may appear. Click **Sign**. ![](/img/bvnk/use-cases/unprocessed/tronlink-11-sign.png) 6. Once the transaction has gone through, open your TronLink extension in Chrome, and next to **Assets**, click the "**+**" sign. ![](/img/bvnk/use-cases/unprocessed/tronlink-12-add-assets.png) 6. In the **Assets** list, in the **New** section, select **LAYER1**, and click the "**+**" sign. ![](/img/bvnk/use-cases/unprocessed/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. --- ## 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 | Use for | Decimals | | :-----------: | :------------:| :-----------------:| :------: | | USD | US Dollar | Deposits / Payouts | 2 | | EUR | Euro | Deposits / Payouts | 2 | | GBP | British Pound | 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 depends on the specific jurisdiction. ::: Currency Code Currency Name Network Required confirmations Use for Decimals Stablecoins EURC Euro Coin ERC20 12 Deposits / Payouts 6 PYUSD PayPal USD ERC20 4 Deposits / Payouts 6 USDC USD Coin ARBITRUM 300 Deposits / Payouts 6 USDC USD Coin BASE 10 Deposits / Payouts 6 USDC USD Coin BEP20 4 Deposits / Payouts 6 USDC USD Coin ERC20 4 Deposits / Payouts 6 USDC USD Coin POLYGON 4 Deposits / Payouts 6 USDC USD Coin SOL 32 Deposits / Payouts 6 USDG Global Dollar SOL 32 Deposits / Payouts 9 USDT Tether USD BEP20 4 Deposits / Payouts 6 USDT Tether USD ERC20 4 Deposits / Payouts 6 USDT Tether USD POLYGON 4 Deposits / Payouts 6 USDT Tether USD TRC20 4 Deposits / Payouts 6 Other BNB Binance Coin BSC 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 SOL 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 ERC20 12 Deposits / Payouts 6 PYUSD PayPal USD ERC20 12 Deposits / Payouts 6 USDC USD Coin ARBITRUM 300 Deposits / Payouts 6 USDC USD Coin BASE 10 Deposits / Payouts 6 USDC USD Coin BEP20 4 Deposits / Payouts 6 USDC USD Coin ERC20 4 Deposits / Payouts 6 USDC USD Coin POLYGON 4 Deposits / Payouts 6 USDC USD Coin SOL 32 Deposits / Payouts 6 USDG Global Dollar SOL 32 Deposits / Payouts 9 USDT Tether USD BEP20 4 Deposits / Payouts 6 USDT Tether USD ERC20 4 Deposits / Payouts 6 USDT Tether USD POLYGON 4 Deposits / Payouts 6 USDT Tether USD TRC20 4 Deposits / Payouts 6 Other BNB Binance Coin BSC 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 SOL Solana 32 Deposits / Payouts 8 TRX Tronix 4 Deposits / Payouts 5 XRP Ripple 1 Deposits / Payouts 6 Currency Code Currency Name Network Required confirmations Testnet Use for Decimals Stablecoins USDT Tether USD ERC20 4 Sepolia Deposits / Payouts 6 USDT Tether USD TRC20 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) • [G](#g) • [H](#h) • [I](#i) • [J](#j) • [K](#k) • [L](#l) • [M](#m) • [N](#n) • [O](#o) • [P](#p) • [Q](#q) • [R](#r) • [S](#s) • [T](#t) • [U](#u) • [V](#v) • [W](#w) • [X](#x) • [Y](#y) • [Z](#z)
## A ### Account ### Address Also, blockchain address or crypto address. A unique identifier on a specific blockchain network where digital assets can be sent or received. ## B ### Beneficiary The entity that receives the transaction. ## C ### Crypto A cryptocurrency is a digital or virtual currency that uses cryptography for security. It is decentralized and operates independently of a central bank. ### Crypto gateway ### 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. ### Customer ### Customer fee ## D ### Digital asset A cryptocurrency asset or a stablecoin. ### Direct model A delivery model where BVNK has a direct relationship with the customer. ## E ### Embedded model A delivery model where BVNK embeds its services into a customer's platform. ### Partner Formerly, Embedded Partner or EP. Business that embeds BVNK's services into their platform. ### Customer Formerly, Embedded Partner Customer or EPC. An Individual or business that uses BVNK's services via an partner. ### Endpoint Also, API endpoint. A URL that allows you to send requests to BVNK's servers. ## F ## G ## H ### Hosted payments page Or Hosted page. A hosted checkout experience embedded in client interface. ## I ## J ## K ## L ## M ### Merchant Customer that uses BVNK's services directly. Same as Account. ### Metadata Additional information about a transaction included in a request or response. ```json Metadata Example { "metadata": { "orderId": "PO-2025-001", "internalUserId": "user-456", "reason": "Customer withdrawal" } } ``` ## N ### Network ## O ### Onboarding The process of setting up a customer to use BVNK's services. 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. ### On-ramp A service that allows you to buy crypto with fiat currency. ### Originator The entity that initiates the transaction. For example, a customer that wants to buy crypto with fiat currency. ## P ### Pay-in A pay-in is a request to move fiat funds from a customer to a wallet. ### Payout A payout is a request to move crypto funds from a wallet to a beneficiary. ### Payment link A link created and shared via BVNK API to collect crypto pay‑ins and process payouts. ### Peg Also, pegged. ### Processing fee A fee charged by BVNK for processing the transaction. ## Q ## R ### Refund A refund is a request to reverse a payment. It is initiated by the originator and is processed by the beneficiary. ### 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 beneficiary could see it. - `customerReference`: Unique identifier of the customer. If 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. ## T ### Transfer Also, internal transfer. A transfer between two wallets within the same account. ## U ## V ### Virtual Account ### Verification of Payee (VoP) VoP is a SEPA (Single Euro Payments Area) service that verifies a beneficiary's name against their IBAN to prevent fraud and errors. ## W ### Wallet ### Wallet profile ### Webhook A URL that allows you to receive notifications from BVNK. See [Create webhook listener](../../get-started/create-webhook-listener) for more information. ## X ## Y ## Z --- ## 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 | | --------- | -------------------------------------------------------------------------------------- | | `reference` | Unique identifier of the industry. For example, "30ea1086-06d8-11ef-b6e1-027612b7f6b5" | | `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". | ### Accommodation and food services ```json { "reference": "30ea1086-06d8-11ef-b6e1-027612b7f6b5", "name": "Accommodation and food services", "children": [ { "reference": "30ea15fc-06d8-11ef-b6e1-027612b7f6b5", "name": "Food/Beverages" }, { "reference": "30ea17fd-06d8-11ef-b6e1-027612b7f6b5", "name": "Hospitality" }, { "reference": "30ea1a02-06d8-11ef-b6e1-027612b7f6b5", "name": "Recreational Facilities/Services (Spas, Saunas etc)" }, { "reference": "30ea5818-06d8-11ef-b6e1-027612b7f6b5", "name": "Supermarkets/Convenience Store" } ] } ``` ```json { "reference": "56e65ebc-06fa-11ef-bbf8-02d3d923cf2b", "name": "Accommodation and food services", "children": [ { "reference": "56e66027-06fa-11ef-bbf8-02d3d923cf2b", "name": "Food/Beverages" }, { "reference": "56e66349-06fa-11ef-bbf8-02d3d923cf2b", "name": "Hospitality" }, { "reference": "56e66697-06fa-11ef-bbf8-02d3d923cf2b", "name": "Recreational Facilities/Services (Spas, Saunas etc)" }, { "reference": "56e66ce7-06fa-11ef-bbf8-02d3d923cf2b", "name": "Supermarkets/Convenience Store" } ] } ``` ### Adult ```json { "reference": "30ea6797-06d8-11ef-b6e1-027612b7f6b5", "name": "Adult", "children": [ { "reference": "30eaed17-06d8-11ef-b6e1-027612b7f6b5", "name": "Escort/Sex workers advertising" }, { "reference": "30ea7552-06d8-11ef-b6e1-027612b7f6b5", "name": "High Street Sex Shop" }, { "reference": "30eb0852-06d8-11ef-b6e1-027612b7f6b5", "name": "Massage Parlours" }, { "reference": "30ebb7bc-06d8-11ef-b6e1-027612b7f6b5", "name": "Online Adult Services" }, { "reference": "30eab905-06d8-11ef-b6e1-027612b7f6b5", "name": "Online Sex Shop" }, { "reference": "30eb1ccf-06d8-11ef-b6e1-027612b7f6b5", "name": "Phone Chat" }, { "reference": "30eb3d15-06d8-11ef-b6e1-027612b7f6b5", "name": "Pornographic video sharing and pornography website" }, { "reference": "30eb7b12-06d8-11ef-b6e1-027612b7f6b5", "name": "Strip Club" } ] } ``` ```json { "reference": "56e66e4c-06fa-11ef-bbf8-02d3d923cf2b", "name": "Adult", "children": [ { "reference": "56e6729e-06fa-11ef-bbf8-02d3d923cf2b", "name": "Escort/Sex workers advertising" }, { "reference": "56e66f7b-06fa-11ef-bbf8-02d3d923cf2b", "name": "High Street Sex Shop" }, { "reference": "56e673ed-06fa-11ef-bbf8-02d3d923cf2b", "name": "Massage Parlours" }, { "reference": "56e6790e-06fa-11ef-bbf8-02d3d923cf2b", "name": "Online Adult Services" }, { "reference": "56e67159-06fa-11ef-bbf8-02d3d923cf2b", "name": "Online Sex Shop" }, { "reference": "56e67536-06fa-11ef-bbf8-02d3d923cf2b", "name": "Phone Chat" }, { "reference": "56e6767c-06fa-11ef-bbf8-02d3d923cf2b", "name": "Pornographic video sharing and pornography website" }, { "reference": "56e677c4-06fa-11ef-bbf8-02d3d923cf2b", "name": "Strip Club" } ] } ``` ### Arms and Defense ```json { "reference": "30ebec70-06d8-11ef-b6e1-027612b7f6b5", "name": "Arms and Defense", "children": [ { "reference": "30ec17af-06d8-11ef-b6e1-027612b7f6b5", "name": "Manufacture and supply of military fighting vehicles" }, { "reference": "30ec4337-06d8-11ef-b6e1-027612b7f6b5", "name": "Manufacture and supply of weapons and ammunition" }, { "reference": "30ec7fb7-06d8-11ef-b6e1-027612b7f6b5", "name": "Military equipment" } ] } ``` ```json { "reference": "56e67a67-06fa-11ef-bbf8-02d3d923cf2b", "name": "Arms and Defense", "children": [ { "reference": "56e67ba5-06fa-11ef-bbf8-02d3d923cf2b", "name": "Manufacture and supply of military fighting vehicles" }, { "reference": "56e67cef-06fa-11ef-bbf8-02d3d923cf2b", "name": "Manufacture and supply of weapons and ammunition" }, { "reference": "56e67e4e-06fa-11ef-bbf8-02d3d923cf2b", "name": "Military equipment" } ] } ``` ### Cash Intensive Business and High Value Dealers ```json { "reference": "30ec901e-06d8-11ef-b6e1-027612b7f6b5", "name": "Cash Intensive Business and High Value Dealers", "children": [ { "reference": "30ed78c0-06d8-11ef-b6e1-027612b7f6b5", "name": "Art Market/Art Collectables/Galleries and Antiques" }, { "reference": "30ee7396-06d8-11ef-b6e1-027612b7f6b5", "name": "Auctioneers" }, { "reference": "30ee9302-06d8-11ef-b6e1-027612b7f6b5", "name": "Builders/Building materials" }, { "reference": "30ecbbde-06d8-11ef-b6e1-027612b7f6b5", "name": "Cash and Valuables-in-Transit" }, { "reference": "30ed4a59-06d8-11ef-b6e1-027612b7f6b5", "name": "Construction" }, { "reference": "30ef0945-06d8-11ef-b6e1-027612b7f6b5", "name": "Fast Moving consumer goods" }, { "reference": "30ecf51a-06d8-11ef-b6e1-027612b7f6b5", "name": "Jewellery/Gold and Diamonds Dealers" }, { "reference": "30edc563-06d8-11ef-b6e1-027612b7f6b5", "name": "Motor Vehicles and Boat Traders" }, { "reference": "30ede2a4-06d8-11ef-b6e1-027612b7f6b5", "name": "Nail Bars/Salon" }, { "reference": "30ece137-06d8-11ef-b6e1-027612b7f6b5", "name": "Precious metals dealers that purchase metals from pawnbrokers and other secondary sources" }, { "reference": "30ee09b8-06d8-11ef-b6e1-027612b7f6b5", "name": "Scrap Metals Dealers/Warehouse" }, { "reference": "30ee2985-06d8-11ef-b6e1-027612b7f6b5", "name": "Storage facilities" }, { "reference": "30eebc77-06d8-11ef-b6e1-027612b7f6b5", "name": "Tour and Travel companies" }, { "reference": "30eed232-06d8-11ef-b6e1-027612b7f6b5", "name": "Used motor vehicles dealers or auctions" }, { "reference": "30ee3a08-06d8-11ef-b6e1-027612b7f6b5", "name": "Wholesale Duty Goods (e.g. Alcohol and tobacco)" } ] } ``` ```json { "reference": "56e67f94-06fa-11ef-bbf8-02d3d923cf2b", "name": "Cash Intensive Business and High Value Dealers", "children": [ { "reference": "56e6a0eb-06fa-11ef-bbf8-02d3d923cf2b", "name": "Art Market/Art Collectables/Galleries and Antiques" }, { "reference": "56e6a8e3-06fa-11ef-bbf8-02d3d923cf2b", "name": "Auctioneers" }, { "reference": "56e6aa27-06fa-11ef-bbf8-02d3d923cf2b", "name": "Builders/Building materials" }, { "reference": "56e680d8-06fa-11ef-bbf8-02d3d923cf2b", "name": "Cash and Valuables-in-Transit" }, { "reference": "56e69f22-06fa-11ef-bbf8-02d3d923cf2b", "name": "Construction" }, { "reference": "56e6aeb1-06fa-11ef-bbf8-02d3d923cf2b", "name": "Fast Moving consumer goods" }, { "reference": "56e69b9f-06fa-11ef-bbf8-02d3d923cf2b", "name": "Jewellery/Gold and Diamonds Dealers" }, { "reference": "56e6a25a-06fa-11ef-bbf8-02d3d923cf2b", "name": "Motor Vehicles and Boat Traders" }, { "reference": "56e6a3a6-06fa-11ef-bbf8-02d3d923cf2b", "name": "Nail Bars/Salon" }, { "reference": "56e68243-06fa-11ef-bbf8-02d3d923cf2b", "name": "Precious metals dealers that purchase metals from pawnbrokers and other secondary sources" }, { "reference": "56e6a4ea-06fa-11ef-bbf8-02d3d923cf2b", "name": "Scrap Metals Dealers/Warehouse" }, { "reference": "56e6a62e-06fa-11ef-bbf8-02d3d923cf2b", "name": "Storage facilities" }, { "reference": "56e6ab63-06fa-11ef-bbf8-02d3d923cf2b", "name": "Tour and Travel companies" }, { "reference": "56e6acaa-06fa-11ef-bbf8-02d3d923cf2b", "name": "Used motor vehicles dealers or auctions" }, { "reference": "56e6a768-06fa-11ef-bbf8-02d3d923cf2b", "name": "Wholesale Duty Goods (e.g. Alcohol and tobacco)" } ] } ``` ### Charities/Non-profit organisation ```json { "reference": "30ef2bc2-06d8-11ef-b6e1-027612b7f6b5", "name": "Charities/Non-profit organisation", "children": [ { "reference": "30ef5367-06d8-11ef-b6e1-027612b7f6b5", "name": "Animal Charities" }, { "reference": "30ef79aa-06d8-11ef-b6e1-027612b7f6b5", "name": "Art and Culture Charities" }, { "reference": "30f02713-06d8-11ef-b6e1-027612b7f6b5", "name": "Education Charities" }, { "reference": "30f044ce-06d8-11ef-b6e1-027612b7f6b5", "name": "Environment Charities" }, { "reference": "30f0572f-06d8-11ef-b6e1-027612b7f6b5", "name": "Health Charities" }, { "reference": "30ef4384-06d8-11ef-b6e1-027612b7f6b5", "name": "Humanitarian support" }, { "reference": "30f06a53-06d8-11ef-b6e1-027612b7f6b5", "name": "International Charities" }, { "reference": "30f0a95b-06d8-11ef-b6e1-027612b7f6b5", "name": "Political Charities" }, { "reference": "30f0abe3-06d8-11ef-b6e1-027612b7f6b5", "name": "Religious Charities" } ] } ``` ```json { "reference": "56e6b142-06fa-11ef-bbf8-02d3d923cf2b", "name": "Charities/Non-profit organisation", "children": [ { "reference": "56e6b416-06fa-11ef-bbf8-02d3d923cf2b", "name": "Animal Charities" }, { "reference": "56e6b54d-06fa-11ef-bbf8-02d3d923cf2b", "name": "Art and Culture Charities" }, { "reference": "56e6b695-06fa-11ef-bbf8-02d3d923cf2b", "name": "Education Charities" }, { "reference": "56e6b7fc-06fa-11ef-bbf8-02d3d923cf2b", "name": "Environment Charities" }, { "reference": "56e6b960-06fa-11ef-bbf8-02d3d923cf2b", "name": "Health Charities" }, { "reference": "56e6b2c0-06fa-11ef-bbf8-02d3d923cf2b", "name": "Humanitarian support" }, { "reference": "56e6baa3-06fa-11ef-bbf8-02d3d923cf2b", "name": "International Charities" }, { "reference": "56e6bbd8-06fa-11ef-bbf8-02d3d923cf2b", "name": "Political Charities" }, { "reference": "56e6bd0a-06fa-11ef-bbf8-02d3d923cf2b", "name": "Religious Charities" } ] } ``` ### Crowdfunding ```json { "reference": "30f0ae43-06d8-11ef-b6e1-027612b7f6b5", "name": "Crowdfunding", "children": [ { "reference": "30f0cc57-06d8-11ef-b6e1-027612b7f6b5", "name": "Donation or rewards platforms" }, { "reference": "30f1213b-06d8-11ef-b6e1-027612b7f6b5", "name": "Investment, equity or debt platforms" }, { "reference": "30f13f60-06d8-11ef-b6e1-027612b7f6b5", "name": "Loan-based or peer-to-peer platforms" }, { "reference": "30f113b0-06d8-11ef-b6e1-027612b7f6b5", "name": "Non-Profit crowdfunding platform" } ] } ``` ```json { "reference": "56e6be94-06fa-11ef-bbf8-02d3d923cf2b", "name": "Crowdfunding", "children": [ { "reference": "56e6bfca-06fa-11ef-bbf8-02d3d923cf2b", "name": "Donation or rewards platforms" }, { "reference": "56e6c242-06fa-11ef-bbf8-02d3d923cf2b", "name": "Investment, equity or debt platforms" }, { "reference": "56e6c37a-06fa-11ef-bbf8-02d3d923cf2b", "name": "Loan-based or peer-to-peer platforms" }, { "reference": "56e6c100-06fa-11ef-bbf8-02d3d923cf2b", "name": "Non-Profit crowdfunding platform" } ] } ``` ### Cryptocurrency business ```json { "reference": "30f15eb2-06d8-11ef-b6e1-027612b7f6b5", "name": "Cryptocurrency business", "children": [ { "reference": "30f19298-06d8-11ef-b6e1-027612b7f6b5", "name": "Crypto OTC Trading" }, { "reference": "30f1a174-06d8-11ef-b6e1-027612b7f6b5", "name": "Cryptocurrency ATM" }, { "reference": "30f1df20-06d8-11ef-b6e1-027612b7f6b5", "name": "Cryptocurrency Investment Advisory" }, { "reference": "30f1a3e2-06d8-11ef-b6e1-027612b7f6b5", "name": "Custodian wallet providers" }, { "reference": "30f20aa4-06d8-11ef-b6e1-027612b7f6b5", "name": "Unlicensed VC exchange" }, { "reference": "30f1f3d0-06d8-11ef-b6e1-027612b7f6b5", "name": "Virtual Currency Administrators or Miners" } ] } ``` ```json { "reference": "56e6c4f5-06fa-11ef-bbf8-02d3d923cf2b", "name": "Cryptocurrency business", "children": [ { "reference": "56e6c689-06fa-11ef-bbf8-02d3d923cf2b", "name": "Crypto OTC Trading" }, { "reference": "56e6c7cf-06fa-11ef-bbf8-02d3d923cf2b", "name": "Cryptocurrency ATM" }, { "reference": "56e6ca53-06fa-11ef-bbf8-02d3d923cf2b", "name": "Cryptocurrency Investment Advisory" }, { "reference": "56e6c913-06fa-11ef-bbf8-02d3d923cf2b", "name": "Custodian wallet providers" }, { "reference": "56e6ccdd-06fa-11ef-bbf8-02d3d923cf2b", "name": "Unlicensed VC exchange" }, { "reference": "56e6cb98-06fa-11ef-bbf8-02d3d923cf2b", "name": "Virtual Currency Administrators or Miners" } ] } ``` ### Entertainment ```json { "reference": "30f29289-06d8-11ef-b6e1-027612b7f6b5", "name": "Entertainment", "children": [ { "reference": "30f29530-06d8-11ef-b6e1-027612b7f6b5", "name": "Media production/online publishing/animation" }, { "reference": "30f2e949-06d8-11ef-b6e1-027612b7f6b5", "name": "Motion pictures/film, broadcast media" }, { "reference": "30f2ec48-06d8-11ef-b6e1-027612b7f6b5", "name": "Movie production" }, { "reference": "30f2d696-06d8-11ef-b6e1-027612b7f6b5", "name": "Music stores, musical instruments" }, { "reference": "30f2d49d-06d8-11ef-b6e1-027612b7f6b5", "name": "Radio, television, stereo" }, { "reference": "30f2d221-06d8-11ef-b6e1-027612b7f6b5", "name": "Social Media platforms" } ] } ``` ```json { "reference": "56e6ce15-06fa-11ef-bbf8-02d3d923cf2b", "name": "Entertainment", "children": [ { "reference": "56e6cf4b-06fa-11ef-bbf8-02d3d923cf2b", "name": "Media production/online publishing/animation" }, { "reference": "56e6dc56-06fa-11ef-bbf8-02d3d923cf2b", "name": "Motion pictures/film, broadcast media" }, { "reference": "56e6dda3-06fa-11ef-bbf8-02d3d923cf2b", "name": "Movie production" }, { "reference": "56e6db06-06fa-11ef-bbf8-02d3d923cf2b", "name": "Music stores, musical instruments" }, { "reference": "56e6d97c-06fa-11ef-bbf8-02d3d923cf2b", "name": "Radio, television, stereo" }, { "reference": "56e6d0f2-06fa-11ef-bbf8-02d3d923cf2b", "name": "Social Media platforms" } ] } ``` ### Extractive Sector ```json { "reference": "30f31328-06d8-11ef-b6e1-027612b7f6b5", "name": "Extractive Sector", "children": [ { "reference": "30f33a2c-06d8-11ef-b6e1-027612b7f6b5", "name": "Extraction of Natural Gas" }, { "reference": "30f315b9-06d8-11ef-b6e1-027612b7f6b5", "name": "Extraction of crude Petroleum" }, { "reference": "30f3693f-06d8-11ef-b6e1-027612b7f6b5", "name": "Mining and Quarrying" } ] } ``` ```json { "reference": "56e6dee8-06fa-11ef-bbf8-02d3d923cf2b", "name": "Extractive Sector", "children": [ { "reference": "56e6e17b-06fa-11ef-bbf8-02d3d923cf2b", "name": "Extraction of Natural Gas" }, { "reference": "56e6e035-06fa-11ef-bbf8-02d3d923cf2b", "name": "Extraction of crude Petroleum" }, { "reference": "56e6e2bc-06fa-11ef-bbf8-02d3d923cf2b", "name": "Mining and Quarrying" } ] } ``` ### Financial Services ```json { "reference": "30f3a0d8-06d8-11ef-b6e1-027612b7f6b5", "name": "Financial Services", "children": [ { "reference": "30f7d0b1-06d8-11ef-b6e1-027612b7f6b5", "name": "Alternative banking platforms (ABPs)" }, { "reference": "30f4b66e-06d8-11ef-b6e1-027612b7f6b5", "name": "Asset Finance" }, { "reference": "30f46a4d-06d8-11ef-b6e1-027612b7f6b5", "name": "Authorised Bank" }, { "reference": "30f47f65-06d8-11ef-b6e1-027612b7f6b5", "name": "Authorised Insurance Firm" }, { "reference": "30f3a36b-06d8-11ef-b6e1-027612b7f6b5", "name": "Authorised Payment Institution (API)" }, { "reference": "30f4e70a-06d8-11ef-b6e1-027612b7f6b5", "name": "Brokerage services to Funds" }, { "reference": "30f3ec75-06d8-11ef-b6e1-027612b7f6b5", "name": "Building Society" }, { "reference": "30f86906-06d8-11ef-b6e1-027612b7f6b5", "name": "CFDs/Forex trading/Brokers" }, { "reference": "30f52efd-06d8-11ef-b6e1-027612b7f6b5", "name": "Consumer Credit Providers" }, { "reference": "30f5457f-06d8-11ef-b6e1-027612b7f6b5", "name": "Corporate Finance" }, { "reference": "30f57fc5-06d8-11ef-b6e1-027612b7f6b5", "name": "Credit Card" }, { "reference": "30f40acb-06d8-11ef-b6e1-027612b7f6b5", "name": "Credit Unions" }, { "reference": "30f59466-06d8-11ef-b6e1-027612b7f6b5", "name": "Discretionary and advisory investment management" }, { "reference": "30f812d5-06d8-11ef-b6e1-027612b7f6b5", "name": "Electronic money institution (EMI)" }, { "reference": "30f61068-06d8-11ef-b6e1-027612b7f6b5", "name": "Execution-only stockbrokers" }, { "reference": "30f630bb-06d8-11ef-b6e1-027612b7f6b5", "name": "Financial Advisors" }, { "reference": "30f85922-06d8-11ef-b6e1-027612b7f6b5", "name": "Informal Value Transfer System (FINCEN definition)" }, { "reference": "30f66497-06d8-11ef-b6e1-027612b7f6b5", "name": "Invoice Finance" }, { "reference": "30f66732-06d8-11ef-b6e1-027612b7f6b5", "name": "Life assurance, and life-related pensions and investment products" }, { "reference": "30f83498-06d8-11ef-b6e1-027612b7f6b5", "name": "Money Services Business (MSB)" }, { "reference": "30f69e50-06d8-11ef-b6e1-027612b7f6b5", "name": "Motor Finance" }, { "reference": "30f6dc3d-06d8-11ef-b6e1-027612b7f6b5", "name": "Name-passing brokers in inter-professional markets" }, { "reference": "30f75d62-06d8-11ef-b6e1-027612b7f6b5", "name": "Non-life providers of investment fund products" }, { "reference": "30f5b372-06d8-11ef-b6e1-027612b7f6b5", "name": "PSD/EMD Agent" }, { "reference": "30f50d6e-06d8-11ef-b6e1-027612b7f6b5", "name": "Penny stock or microcap securities (US SEC definition)" }, { "reference": "30f760b3-06d8-11ef-b6e1-027612b7f6b5", "name": "Private Equity" }, { "reference": "30f837aa-06d8-11ef-b6e1-027612b7f6b5", "name": "Retail Banking" }, { "reference": "30f4e9bb-06d8-11ef-b6e1-027612b7f6b5", "name": "Shell Banks" }, { "reference": "30f79309-06d8-11ef-b6e1-027612b7f6b5", "name": "Syndicated Loan" }, { "reference": "30f7b3ac-06d8-11ef-b6e1-027612b7f6b5", "name": "Trade Finance" }, { "reference": "30f854ec-06d8-11ef-b6e1-027612b7f6b5", "name": "Unlicensed MSB" }, { "reference": "30f4343b-06d8-11ef-b6e1-027612b7f6b5", "name": "Unregulated Foreign Exchange Provider" }, { "reference": "30f3e91f-06d8-11ef-b6e1-027612b7f6b5", "name": "Virtual Asset Service Providers (VASPs)" }, { "reference": "30f3a5b3-06d8-11ef-b6e1-027612b7f6b5", "name": "Wealth Management and Private Bank" }, { "reference": "30f85769-06d8-11ef-b6e1-027612b7f6b5", "name": "Wholesale Banking - Capital Markets" }, { "reference": "30f7cdb3-06d8-11ef-b6e1-027612b7f6b5", "name": "Wholesale Markets" } ] } ``` ```json { "reference": "56e6e3f9-06fa-11ef-bbf8-02d3d923cf2b", "name": "Financial Services", "children": [ { "reference": "56e70c81-06fa-11ef-bbf8-02d3d923cf2b", "name": "Alternative banking platforms (ABPs)" }, { "reference": "56e6f099-06fa-11ef-bbf8-02d3d923cf2b", "name": "Asset Finance" }, { "reference": "56e6eda5-06fa-11ef-bbf8-02d3d923cf2b", "name": "Authorised Bank" }, { "reference": "56e6ef22-06fa-11ef-bbf8-02d3d923cf2b", "name": "Authorised Insurance Firm" }, { "reference": "56e6e528-06fa-11ef-bbf8-02d3d923cf2b", "name": "Authorised Payment Institution (API)" }, { "reference": "56e6f20e-06fa-11ef-bbf8-02d3d923cf2b", "name": "Brokerage services to Funds" }, { "reference": "56e6e8d5-06fa-11ef-bbf8-02d3d923cf2b", "name": "Building Society" }, { "reference": "56e71ee0-06fa-11ef-bbf8-02d3d923cf2b", "name": "CFDs/Forex trading/Brokers" }, { "reference": "56e6f6c3-06fa-11ef-bbf8-02d3d923cf2b", "name": "Consumer Credit Providers" }, { "reference": "56e6f83d-06fa-11ef-bbf8-02d3d923cf2b", "name": "Corporate Finance" }, { "reference": "56e6fa51-06fa-11ef-bbf8-02d3d923cf2b", "name": "Credit Card" }, { "reference": "56e6ea55-06fa-11ef-bbf8-02d3d923cf2b", "name": "Credit Unions" }, { "reference": "56e6fbd7-06fa-11ef-bbf8-02d3d923cf2b", "name": "Discretionary and advisory investment management" }, { "reference": "56e70dcb-06fa-11ef-bbf8-02d3d923cf2b", "name": "Electronic money institution (EMI)" }, { "reference": "56e6fed7-06fa-11ef-bbf8-02d3d923cf2b", "name": "Execution-only stockbrokers" }, { "reference": "56e7001b-06fa-11ef-bbf8-02d3d923cf2b", "name": "Financial Advisors" }, { "reference": "56e71d97-06fa-11ef-bbf8-02d3d923cf2b", "name": "Informal Value Transfer System (FINCEN definition)" }, { "reference": "56e70151-06fa-11ef-bbf8-02d3d923cf2b", "name": "Invoice Finance" }, { "reference": "56e70289-06fa-11ef-bbf8-02d3d923cf2b", "name": "Life assurance, and life-related pensions and investment products" }, { "reference": "56e70f2d-06fa-11ef-bbf8-02d3d923cf2b", "name": "Money Services Business (MSB)" }, { "reference": "56e703cc-06fa-11ef-bbf8-02d3d923cf2b", "name": "Motor Finance" }, { "reference": "56e70508-06fa-11ef-bbf8-02d3d923cf2b", "name": "Name-passing brokers in inter-professional markets" }, { "reference": "56e7064b-06fa-11ef-bbf8-02d3d923cf2b", "name": "Non-life providers of investment fund products" }, { "reference": "56e6fd58-06fa-11ef-bbf8-02d3d923cf2b", "name": "PSD/EMD Agent" }, { "reference": "56e6f54a-06fa-11ef-bbf8-02d3d923cf2b", "name": "Penny stock or microcap securities (US SEC definition)" }, { "reference": "56e70791-06fa-11ef-bbf8-02d3d923cf2b", "name": "Private Equity" }, { "reference": "56e716fc-06fa-11ef-bbf8-02d3d923cf2b", "name": "Retail Banking" }, { "reference": "56e6f3cd-06fa-11ef-bbf8-02d3d923cf2b", "name": "Shell Banks" }, { "reference": "56e708cb-06fa-11ef-bbf8-02d3d923cf2b", "name": "Syndicated Loan" }, { "reference": "56e70a01-06fa-11ef-bbf8-02d3d923cf2b", "name": "Trade Finance" }, { "reference": "56e71a80-06fa-11ef-bbf8-02d3d923cf2b", "name": "Unlicensed MSB" }, { "reference": "56e6ec13-06fa-11ef-bbf8-02d3d923cf2b", "name": "Unregulated Foreign Exchange Provider" }, { "reference": "56e6e79d-06fa-11ef-bbf8-02d3d923cf2b", "name": "Virtual Asset Service Providers (VASPs)" }, { "reference": "56e6e669-06fa-11ef-bbf8-02d3d923cf2b", "name": "Wealth Management and Private Bank" }, { "reference": "56e71c40-06fa-11ef-bbf8-02d3d923cf2b", "name": "Wholesale Banking - Capital Markets" }, { "reference": "56e70b43-06fa-11ef-bbf8-02d3d923cf2b", "name": "Wholesale Markets" } ] } ``` ### Gambling Business ```json { "reference": "30f89d6d-06d8-11ef-b6e1-027612b7f6b5", "name": "Gambling Business", "children": [ { "reference": "30f8a1d9-06d8-11ef-b6e1-027612b7f6b5", "name": "Arcades" }, { "reference": "30f8f01e-06d8-11ef-b6e1-027612b7f6b5", "name": "Bettings" }, { "reference": "30f94758-06d8-11ef-b6e1-027612b7f6b5", "name": "Bingo" }, { "reference": "30f9b94b-06d8-11ef-b6e1-027612b7f6b5", "name": "Casino" }, { "reference": "30f98d5e-06d8-11ef-b6e1-027612b7f6b5", "name": "Casino - ship based, poker" }, { "reference": "30f9cc9e-06d8-11ef-b6e1-027612b7f6b5", "name": "Crypto Gambling (e.g., bitcoin Casino)" }, { "reference": "30fa23af-06d8-11ef-b6e1-027612b7f6b5", "name": "Fantasy sports, iGaming, video games" }, { "reference": "30f9fc37-06d8-11ef-b6e1-027612b7f6b5", "name": "Gambling software" }, { "reference": "30fa0e37-06d8-11ef-b6e1-027612b7f6b5", "name": "Gaming machine" }, { "reference": "30fa1e4c-06d8-11ef-b6e1-027612b7f6b5", "name": "Lotteries" }, { "reference": "30fa2141-06d8-11ef-b6e1-027612b7f6b5", "name": "Remote/Online" }, { "reference": "30f89f97-06d8-11ef-b6e1-027612b7f6b5", "name": "Sports Betting" }, { "reference": "30fa469c-06d8-11ef-b6e1-027612b7f6b5", "name": "Unlicensed Gambling business (remote and non-remote)" } ] } ``` ```json { "reference": "56e72020-06fa-11ef-bbf8-02d3d923cf2b", "name": "Gambling Business", "children": [ { "reference": "56e722bd-06fa-11ef-bbf8-02d3d923cf2b", "name": "Arcades" }, { "reference": "56e723f5-06fa-11ef-bbf8-02d3d923cf2b", "name": "Bettings" }, { "reference": "56e72589-06fa-11ef-bbf8-02d3d923cf2b", "name": "Bingo" }, { "reference": "56e7283c-06fa-11ef-bbf8-02d3d923cf2b", "name": "Casino" }, { "reference": "56e726d9-06fa-11ef-bbf8-02d3d923cf2b", "name": "Casino - ship based, poker" }, { "reference": "56e7297e-06fa-11ef-bbf8-02d3d923cf2b", "name": "Crypto Gambling (e.g., bitcoin Casino)" }, { "reference": "56e7302a-06fa-11ef-bbf8-02d3d923cf2b", "name": "Fantasy sports, iGaming, video games" }, { "reference": "56e72ac8-06fa-11ef-bbf8-02d3d923cf2b", "name": "Gambling software" }, { "reference": "56e72c37-06fa-11ef-bbf8-02d3d923cf2b", "name": "Gaming machine" }, { "reference": "56e72d97-06fa-11ef-bbf8-02d3d923cf2b", "name": "Lotteries" }, { "reference": "56e72ee8-06fa-11ef-bbf8-02d3d923cf2b", "name": "Remote/Online" }, { "reference": "56e72159-06fa-11ef-bbf8-02d3d923cf2b", "name": "Sports Betting" }, { "reference": "56e731aa-06fa-11ef-bbf8-02d3d923cf2b", "name": "Unlicensed Gambling business (remote and non-remote)" } ] } ``` ### Independent legal professionals (firm and sole practitioner) ```json { "reference": "30fa59c8-06d8-11ef-b6e1-027612b7f6b5", "name": "Independent legal professionals (firm and sole practitioner)", "children": [ { "reference": "30fad312-06d8-11ef-b6e1-027612b7f6b5", "name": "Accountancy Services" }, { "reference": "30fa6c0c-06d8-11ef-b6e1-027612b7f6b5", "name": "Auditor" }, { "reference": "30fae32a-06d8-11ef-b6e1-027612b7f6b5", "name": "Corporate Structures formation company" }, { "reference": "30fb124c-06d8-11ef-b6e1-027612b7f6b5", "name": "External Accountant" }, { "reference": "30fb4033-06d8-11ef-b6e1-027612b7f6b5", "name": "Individuals who provide tax advice and/or aggressive tax schemes" }, { "reference": "30fa6f39-06d8-11ef-b6e1-027612b7f6b5", "name": "Insolvency practitioner" }, { "reference": "30fb8a58-06d8-11ef-b6e1-027612b7f6b5", "name": "Legal Services Firm" }, { "reference": "30fa8ecd-06d8-11ef-b6e1-027612b7f6b5", "name": "Notaries" }, { "reference": "30fbac75-06d8-11ef-b6e1-027612b7f6b5", "name": "Tax Adviser" }, { "reference": "30fab353-06d8-11ef-b6e1-027612b7f6b5", "name": "Trust and Company Services Providers (TCSPs)" } ] } ``` ```json { "reference": "56e7334a-06fa-11ef-bbf8-02d3d923cf2b", "name": "Independent legal professionals (firm and sole practitioner)", "children": [ { "reference": "56e73b45-06fa-11ef-bbf8-02d3d923cf2b", "name": "Accountancy Services" }, { "reference": "56e73483-06fa-11ef-bbf8-02d3d923cf2b", "name": "Auditor" }, { "reference": "56e73ca4-06fa-11ef-bbf8-02d3d923cf2b", "name": "Corporate Structures formation company" }, { "reference": "56e73dec-06fa-11ef-bbf8-02d3d923cf2b", "name": "External Accountant" }, { "reference": "56e73f2d-06fa-11ef-bbf8-02d3d923cf2b", "name": "Individuals who provide tax advice and/or aggressive tax schemes" }, { "reference": "56e7366b-06fa-11ef-bbf8-02d3d923cf2b", "name": "Insolvency practitioner" }, { "reference": "56e74073-06fa-11ef-bbf8-02d3d923cf2b", "name": "Legal Services Firm" }, { "reference": "56e737f9-06fa-11ef-bbf8-02d3d923cf2b", "name": "Notaries" }, { "reference": "56e741fa-06fa-11ef-bbf8-02d3d923cf2b", "name": "Tax Adviser" }, { "reference": "56e73970-06fa-11ef-bbf8-02d3d923cf2b", "name": "Trust and Company Services Providers (TCSPs)" } ] } ``` ### Manufacturing ```json { "reference": "30fc4d70-06d8-11ef-b6e1-027612b7f6b5", "name": "Manufacturing", "children": [ { "reference": "30fc76a0-06d8-11ef-b6e1-027612b7f6b5", "name": "Automotive, accessories" }, { "reference": "30fcaca1-06d8-11ef-b6e1-027612b7f6b5", "name": "Electronic manufacturing, consumer electronics" }, { "reference": "30fc9849-06d8-11ef-b6e1-027612b7f6b5", "name": "Railroad manufacture" }, { "reference": "30fc78b1-06d8-11ef-b6e1-027612b7f6b5", "name": "Shipbuilding, heavy machinery" }, { "reference": "30fc5ef1-06d8-11ef-b6e1-027612b7f6b5", "name": "Telecommunications, mechanical, industrial engineering, semiconductors" } ] } ``` ```json { "reference": "56e74bfd-06fa-11ef-bbf8-02d3d923cf2b", "name": "Manufacturing", "children": [ { "reference": "56e7507a-06fa-11ef-bbf8-02d3d923cf2b", "name": "Automotive, accessories" }, { "reference": "56e754a0-06fa-11ef-bbf8-02d3d923cf2b", "name": "Electronic manufacturing, consumer electronics" }, { "reference": "56e75348-06fa-11ef-bbf8-02d3d923cf2b", "name": "Railroad manufacture" }, { "reference": "56e751f0-06fa-11ef-bbf8-02d3d923cf2b", "name": "Shipbuilding, heavy machinery" }, { "reference": "56e74d2d-06fa-11ef-bbf8-02d3d923cf2b", "name": "Telecommunications, mechanical, industrial engineering, semiconductors" } ] } ``` ### Online Services ```json { "reference": "30fcda71-06d8-11ef-b6e1-027612b7f6b5", "name": "Online Services", "children": [ { "reference": "30fd90ec-06d8-11ef-b6e1-027612b7f6b5", "name": "Network security" }, { "reference": "30fd928c-06d8-11ef-b6e1-027612b7f6b5", "name": "Online service providers" }, { "reference": "30fd8ec8-06d8-11ef-b6e1-027612b7f6b5", "name": "Streaming, hosting, cloud services" } ] } ``` ```json { "reference": "56e755ef-06fa-11ef-bbf8-02d3d923cf2b", "name": "Online Services", "children": [ { "reference": "56e75897-06fa-11ef-bbf8-02d3d923cf2b", "name": "Network security" }, { "reference": "56e759f9-06fa-11ef-bbf8-02d3d923cf2b", "name": "Online service providers" }, { "reference": "56e75742-06fa-11ef-bbf8-02d3d923cf2b", "name": "Streaming, hosting, cloud services" } ] } ``` ### Other Companies ```json { "reference": "30fd93e7-06d8-11ef-b6e1-027612b7f6b5", "name": "Other Companies", "children": [ { "reference": "30fdce2c-06d8-11ef-b6e1-027612b7f6b5", "name": "Aviation/Aerospace/airline" }, { "reference": "30fdbd8f-06d8-11ef-b6e1-027612b7f6b5", "name": "Business Consultancy" }, { "reference": "30fdcb6a-06d8-11ef-b6e1-027612b7f6b5", "name": "Chemicals/Biotechnology" }, { "reference": "30fdb6bb-06d8-11ef-b6e1-027612b7f6b5", "name": "Commodity Trading Companies" }, { "reference": "30fdc9e7-06d8-11ef-b6e1-027612b7f6b5", "name": "Computers, hardware, software, IT" }, { "reference": "30fdbbfb-06d8-11ef-b6e1-027612b7f6b5", "name": "Dating/Matchmaking websites" }, { "reference": "30fdbefb-06d8-11ef-b6e1-027612b7f6b5", "name": "Direct Selling (MLM)" }, { "reference": "30fdba4d-06d8-11ef-b6e1-027612b7f6b5", "name": "Fashion, cloth, shoes, apparel, clothing stores, leather goods" }, { "reference": "30fdc20b-06d8-11ef-b6e1-027612b7f6b5", "name": "Football Sector" }, { "reference": "30fdc050-06d8-11ef-b6e1-027612b7f6b5", "name": "Freelance Platforms/GIG Economy" }, { "reference": "30fdc364-06d8-11ef-b6e1-027612b7f6b5", "name": "General trading companies operating in free trade zones" }, { "reference": "30fdc6ca-06d8-11ef-b6e1-027612b7f6b5", "name": "Graphic Design/Web design" }, { "reference": "30fdb8ac-06d8-11ef-b6e1-027612b7f6b5", "name": "Holding Companies" }, { "reference": "30fdcf8c-06d8-11ef-b6e1-027612b7f6b5", "name": "Human resources/staffing/recruiting/payroll services" }, { "reference": "30fdc525-06d8-11ef-b6e1-027612b7f6b5", "name": "Marine services, fishery" }, { "reference": "30fdccdc-06d8-11ef-b6e1-027612b7f6b5", "name": "Packing, containers, warehousing, facilities services/logistics" }, { "reference": "30fdc85c-06d8-11ef-b6e1-027612b7f6b5", "name": "Printing/Photography/Publishing" } ] } ``` ```json { "reference": "56e75b69-06fa-11ef-bbf8-02d3d923cf2b", "name": "Other Companies", "children": [ { "reference": "56e774af-06fa-11ef-bbf8-02d3d923cf2b", "name": "Aviation/Aerospace/airline" }, { "reference": "56e764bf-06fa-11ef-bbf8-02d3d923cf2b", "name": "Business Consultancy" }, { "reference": "56e77226-06fa-11ef-bbf8-02d3d923cf2b", "name": "Chemicals/Biotechnology" }, { "reference": "56e75cb6-06fa-11ef-bbf8-02d3d923cf2b", "name": "Commodity Trading Companies" }, { "reference": "56e770a3-06fa-11ef-bbf8-02d3d923cf2b", "name": "Computers, hardware, software, IT" }, { "reference": "56e7620d-06fa-11ef-bbf8-02d3d923cf2b", "name": "Dating/Matchmaking websites" }, { "reference": "56e766b6-06fa-11ef-bbf8-02d3d923cf2b", "name": "Direct Selling (MLM)" }, { "reference": "56e7604f-06fa-11ef-bbf8-02d3d923cf2b", "name": "Fashion, cloth, shoes, apparel, clothing stores, leather goods" }, { "reference": "56e769b7-06fa-11ef-bbf8-02d3d923cf2b", "name": "Football Sector" }, { "reference": "56e76874-06fa-11ef-bbf8-02d3d923cf2b", "name": "Freelance Platforms/GIG Economy" }, { "reference": "56e76b3a-06fa-11ef-bbf8-02d3d923cf2b", "name": "General trading companies operating in free trade zones" }, { "reference": "56e76e1b-06fa-11ef-bbf8-02d3d923cf2b", "name": "Graphic Design/Web design" }, { "reference": "56e75e57-06fa-11ef-bbf8-02d3d923cf2b", "name": "Holding Companies" }, { "reference": "56e775ec-06fa-11ef-bbf8-02d3d923cf2b", "name": "Human resources/staffing/recruiting/payroll services" }, { "reference": "56e76c91-06fa-11ef-bbf8-02d3d923cf2b", "name": "Marine services, fishery" }, { "reference": "56e7736e-06fa-11ef-bbf8-02d3d923cf2b", "name": "Packing, containers, warehousing, facilities services/logistics" }, { "reference": "56e76f64-06fa-11ef-bbf8-02d3d923cf2b", "name": "Printing/Photography/Publishing" } ] } ``` ### Pharmaceutical and Medicine ```json { "reference": "30fdd12f-06d8-11ef-b6e1-027612b7f6b5", "name": "Pharmaceutical and Medicine", "children": [ { "reference": "30fdd491-06d8-11ef-b6e1-027612b7f6b5", "name": "Alternative medicines" }, { "reference": "30fdd5fc-06d8-11ef-b6e1-027612b7f6b5", "name": "Cannabidiol (CBD) Distributor" }, { "reference": "30fdd76c-06d8-11ef-b6e1-027612b7f6b5", "name": "Cannabidiol (CBD) Manufacturer" }, { "reference": "30fdd8d6-06d8-11ef-b6e1-027612b7f6b5", "name": "Cannabidiol (CBD) Retailer" }, { "reference": "30fdda4d-06d8-11ef-b6e1-027612b7f6b5", "name": "Medical, dental equipment and supplies" }, { "reference": "30fdd2d5-06d8-11ef-b6e1-027612b7f6b5", "name": "Nutraceuticals" } ] } ``` ```json { "reference": "56e77742-06fa-11ef-bbf8-02d3d923cf2b", "name": "Pharmaceutical and Medicine", "children": [ { "reference": "56e779a3-06fa-11ef-bbf8-02d3d923cf2b", "name": "Alternative medicines" }, { "reference": "56e77ae8-06fa-11ef-bbf8-02d3d923cf2b", "name": "Cannabidiol (CBD) Distributor" }, { "reference": "56e77c27-06fa-11ef-bbf8-02d3d923cf2b", "name": "Cannabidiol (CBD) Manufacturer" }, { "reference": "56e77d65-06fa-11ef-bbf8-02d3d923cf2b", "name": "Cannabidiol (CBD) Retailer" }, { "reference": "56e77e9e-06fa-11ef-bbf8-02d3d923cf2b", "name": "Medical, dental equipment and supplies" }, { "reference": "56e77872-06fa-11ef-bbf8-02d3d923cf2b", "name": "Nutraceuticals" } ] } ``` ### Public Sector ```json { "reference": "30fddbc8-06d8-11ef-b6e1-027612b7f6b5", "name": "Public Sector", "children": [ { "reference": "30fde5a5-06d8-11ef-b6e1-027612b7f6b5", "name": "Civic/social organization/defence and space" }, { "reference": "30fddd22-06d8-11ef-b6e1-027612b7f6b5", "name": "Consulate" }, { "reference": "30fdde9a-06d8-11ef-b6e1-027612b7f6b5", "name": "Diplomatic mission" }, { "reference": "30fde018-06d8-11ef-b6e1-027612b7f6b5", "name": "Embassies" }, { "reference": "30fde2d2-06d8-11ef-b6e1-027612b7f6b5", "name": "Govt administration/relations/Judiciary/Legislative office" }, { "reference": "30fde431-06d8-11ef-b6e1-027612b7f6b5", "name": "International Affairs, Trade and development" }, { "reference": "30fde184-06d8-11ef-b6e1-027612b7f6b5", "name": "Museums" } ] } ``` ```json { "reference": "56e77fd7-06fa-11ef-bbf8-02d3d923cf2b", "name": "Public Sector", "children": [ { "reference": "56e78875-06fa-11ef-bbf8-02d3d923cf2b", "name": "Civic/social organization/defence and space" }, { "reference": "56e78101-06fa-11ef-bbf8-02d3d923cf2b", "name": "Consulate" }, { "reference": "56e78239-06fa-11ef-bbf8-02d3d923cf2b", "name": "Diplomatic mission" }, { "reference": "56e7836e-06fa-11ef-bbf8-02d3d923cf2b", "name": "Embassies" }, { "reference": "56e785dc-06fa-11ef-bbf8-02d3d923cf2b", "name": "Govt administration/relations/Judiciary/Legislative office" }, { "reference": "56e78721-06fa-11ef-bbf8-02d3d923cf2b", "name": "International Affairs, Trade and development" }, { "reference": "56e784a8-06fa-11ef-bbf8-02d3d923cf2b", "name": "Museums" } ] } ``` ### Real Estate Activities ```json { "reference": "30fbaea3-06d8-11ef-b6e1-027612b7f6b5", "name": "Real Estate Activities", "children": [ { "reference": "30fbc49a-06d8-11ef-b6e1-027612b7f6b5", "name": "Other letting and operating of own or leased real estate" }, { "reference": "30fbc985-06d8-11ef-b6e1-027612b7f6b5", "name": "Real estate agencies" }, { "reference": "30fbdb1d-06d8-11ef-b6e1-027612b7f6b5", "name": "Renting and operating of Housing Association real estate" } ] } ``` ```json { "reference": "56e74334-06fa-11ef-bbf8-02d3d923cf2b", "name": "Real Estate Activities", "children": [ { "reference": "56e74470-06fa-11ef-bbf8-02d3d923cf2b", "name": "Other letting and operating of own or leased real estate" }, { "reference": "56e745c6-06fa-11ef-bbf8-02d3d923cf2b", "name": "Real estate agencies" }, { "reference": "56e74708-06fa-11ef-bbf8-02d3d923cf2b", "name": "Renting and operating of Housing Association real estate" } ] } ``` ### Wildlife ```json { "reference": "30fbef1e-06d8-11ef-b6e1-027612b7f6b5", "name": "Wildlife", "children": [ { "reference": "30fc0ad0-06d8-11ef-b6e1-027612b7f6b5", "name": "Illegal Wildlife Trade" }, { "reference": "30fc3549-06d8-11ef-b6e1-027612b7f6b5", "name": "Wildlife Trade" } ] } ``` ```json { "reference": "56e74845-06fa-11ef-bbf8-02d3d923cf2b", "name": "Wildlife", "children": [ { "reference": "56e7497a-06fa-11ef-bbf8-02d3d923cf2b", "name": "Illegal Wildlife Trade" }, { "reference": "56e74ac7-06fa-11ef-bbf8-02d3d923cf2b", "name": "Wildlife Trade" } ] } ``` --- ## 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 | | `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 | | `FEDNOW` | Federal Reserve instant payment system providing 24/7 real-time payment capabilities. | Instant | 24/7 | USD | | 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 | | 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 €100,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", ... } ``` --- ## Receive fiat funds via SWIFT To receive fiat funds in your BVNK virtual account, you can deposit EUR, GBP, or USD from a registered bank account. This guide explains how SWIFT wallets work and how to obtain the deposit details your senders need. SWIFT wallets at BVNK are **pooled accounts**. Multiple customers share a consolidated SWIFT account, and BVNK uses the payment reference to allocate incoming funds to the correct wallet. --- ## How SWIFT pooled accounts work When you receive a SWIFT pay-in: 1. The sender initiates a payment from their bank to the BVNK SWIFT account. 2. BVNK receives the payment and checks the payment reference field. 3. Based on the reference, BVNK assigns the payment to the correct 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. --- ## Prerequisites Before depositing funds, ensure you have: - A fiat wallet with the SWIFT payment method enabled. See [Create a wallet](../../../get-started/creating-your-first-wallet). - A registered bank account on your BVNK Settings page (for Portal deposits). --- ## Get deposit details Retrieve the deposit details to share with the sender. To get your wallet's deposit details via API, send the [`GET /ledger/v1/wallets/{id}`](../../../api-explorer/endpoints/ledger-wallet-read) request: In the response, locate the `ledgers` array. For fiat wallets, extract the `accountNumber`, `code`, and `paymentReference` fields. ```json { "id": "a:24092328494070:G5i4XZ9:1", "accountReference": "3399c975-e1c1-4acf-9a90-6cfbdcdeaaea", "customerReference": "b94dcf2a-d377-469e-8846-e21f65b18a77", "name": "USD SWIFT Wallet", "status": "ACTIVE", "balance": { "value": 5000.00, "currencyCode": "USD" }, "ledgers": [ { "type": "FIAT", "accountHolderName": "System Pay Services (Malta) Limited", "accountNumber": "ES8201828510940200010371", // highlight-start "code": "BBVAESMMXXX", "accountNumberFormat": "SWIFT", "paymentReference": "REF DZ0XJL4", // highlight-end "bank": { "identificationCode": "BBVAESMMXXX", "name": "Banco Bilbao Vizcaya Argentaria, S.A.", "address": { "addressLine1": "De San Nicolas 4", "city": "Bilboa", "postCode": "48005", "country": "ES" } } } ] } ``` To find your deposit details in the BVNK Portal, follow these steps: 1. Go to Portal and navigate to the **Wallets** section. 2. In the **Wallets** list, locate the fiat wallet you want to fund and select it. 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 ![](/img/bvnk/use-cases/deposit-details.png) --- ## Provide payment instructions to senders Provide the following details to anyone sending funds to your SWIFT wallet: | Parameter | API field name | Description | | :---- | :---------- | :---------- | | Account number | `accountNumber` | The IBAN or account number for the BVNK SWIFT account. | | SWIFT/BIC code | `code` | The bank identifier code for the receiving bank. | | Payment reference | `paymentReference` | Unique reference for your account. Use this as the payment reference when instructing senders. | :::info Instruct external senders to include the `paymentReference` when transferring funds to your BVNK SWIFT wallet. The sending bank must include this value in the payment instructions to link the payment to your account. Ensure senders use the same `paymentReference` for all future transfers to your BVNK wallet. ::: --- ## What happens without a payment reference If the sender omits the payment reference: - BVNK cannot automatically assign the payment to your wallet. - You must provide proof of payment (such as a bank statement or transfer confirmation) to BVNK support. - After verification, BVNK manually credits the funds to your wallet. This process may delay the availability of your funds. --- ## Test in sandbox To simulate a SWIFT pay-in in the sandbox environment, send the [`POST /payment/v2/payins/simulation`](../../../api-explorer/endpoints/simulate-payin-v-2) request: ```json { "id": "a:25112856360743:5paGBL3:1", "accountReference": "b94c42ec-f87f-4ecb-a0ce-01182fe59eb8", "customerReference": "ab3529ed-29c2-447b-947f-fd5b4eb83a37", "name": "EUR SWIFT", "status": "ACTIVE", "balance": { "value": 0.00, "currencyCode": "EUR" }, "ledgers": [ { "type": "FIAT", "accountHolderName": "System Pay Services (Malta) Limited", "accountNumber": "ES8201828510940200010371", "code": "BBVAESMMXXX", "accountNumberFormat": "SWIFT", "paymentReference": "REF DZ0XJL4", "bank": { "identificationCode": "BBVAESMMXXX", "name": "Banco Bilbao Vizcaya Argentaria, S.A.", "address": { "addressLine1": "De San Nicolas 4", "city": "Bilboa", "postCode": "48005", "country": "ES" } } } ] } ``` For more simulation options, see [Try fiat payments in simulator](../../../get-started/payment-sim). --- **What's next?** - [Send payments to customers](../initiate-payment-2) - [Refund fiat pay-ins](../refund-payments) - [Configure webhooks](../../../get-started/create-webhook-listener) to receive real-time notifications about incoming payments --- ## Send payments to customers 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: SORT * 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. ## 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": "CHASUS33", "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" } } } ``` Note the following fields: | Parameter | Required | Description | | :-------- | :-------: | :---------- | | `reference` | :x: | Optional. The internal reference is shown in the CSV reports for custom use cases. Can be a UUID or any string to identify the payment.**The field is for information purposes only. It will be fully supported in future releases.** | | `method` | :x: | Payment method. Mandatory for payouts in USD and optional for other currencies.We recommend leaving it blank, so that the fastest method is selected automatically.Depending on the specific wallet and currency, not all methods can be available for the transaction. Available options:* SEPA_INST (EUR)* SEPA_CT (EUR)* ACH (USD)* ACH_SAME_DAY (USD)* FASTER_PAYMENT (GBP)* SWIFT (USD, EUR, GBP)* FEDWIRE (USD)* BOOK (USD)* RTP (USD)* FEDNOW (USD) | | `beneficiary.remittanceInformation` | :white_check_mark: | Payment reference that beneficiaries see in their bank statements when receiving the payment.Can contain only alphanumeric characters (a-z, A-Z, 0-9), whitespaces, or the `.,-` symbols.Other allowed characters vary depending on the used payment method or currency. | | `beneficiary.address.code` | :white_check_mark: | Country code of the beneficiary's address. The beneficiary address must include at least this field. | | `bankAccount.accountType` | :x: | Type of the account. Optional. Only for US banks. | | `bankAccount.bank.identificationCode` | :white_check_mark: | Bank's identification number, for example, BIC for UK banks, SORT code, ABA routing number, and so on.Optional for local payouts in EUR. | | `bankAccount.bank.nid` | :x: | National bank identification code: country-specific code assigned to a financial institution by its central bank or banking authority. In most cases, used for SWIFT payments to US accounts. | | `requestDetails.originator.ipAddress` | :x: | IP address of the customer initiating the payout. Required when you [make a payment via a customer's wallet](../../stablecoin-payments-for-platforms/send-stabelcoin-via-customers-wallet). | 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. For the detailed description of all fields, see the [`POST /payment/v2/payouts`](../../../api-explorer/endpoints/payout-create-v-2) API reference. 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": "CHASUS33" } } }, "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" } ``` | Parameter | Description | | :------------------ | :------------------------------------------------------------------------------------------------------------------ | | `type` | Type of the response. | | `originator.amount` | Amount of funds sent to the beneficiary. For fiat transactions, it will always be the same as `beneficiary.amount`. | 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": "GB29NWBK60161331926819", "bankCode": "NWBKGB2L", "firstName": "John", "lastName": "Smith", "businessName": "Acme Corporation Ltd", "currency": "GBP" } ``` In the request body, provide the following fields: | Field | Description | | :---- | :---------- | | `accountNumber` | The beneficiary's bank account number. | | `bankCode` | The beneficiary's bank code. | | `firstName` | The beneficiary's first name. Only for `"type": "INDIVIDUAL"`. | | `lastName` | The beneficiary's last name. Only for `"type": "INDIVIDUAL"`. | | `businessName` | The beneficiary's business name. Only for `"type": "COMPANY"`. | | `currency` | The currency of the beneficiary's bank account. | 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 } ``` Note the following fields: | Field | Description | | :---- | :---------- | | `matchStatus` | Status of the name verification. The following name statuses are available:* `MATCH`* `PARTIAL_MATCH`* `NO_MATCH`* `NOT_REQUIRED`* `NOT_POSSIBLE`* `TECHNICAL_ERROR` | | `validUntil` | Time period during which the verification request is valid. By default, five minutes. | 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": "GB", "postCode": "EC2R 8AH", "fullAddress": "123 Main St Suite 500" }, "bankAccount": { "format": "IBAN", "bankName": "NatWest Bank", "accountNumber": "GB29NWBK60161331926819", "bankCode": "NWBKGB2L", "bankAddress": { "addressLine1": "123 Main St", "addressLine2": "Suite 500", "region": "London", "city": "London", "country": "GB", "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) --- ## Virtual account eligibility Virtual accounts require additional approval from BVNK's compliance team. To qualify, your business must: - Be a fully licensed financial services provider (for example, UK FCA license, Canadian MSB). - Demonstrate a robust financial crime control framework including AML policies, risk management, and transaction monitoring. - Complete compliance checks with documentation review and interviews. Contact your Account Manager to initiate the virtual account approval process. After approval, BVNK's commercial team configures your VA product and maps your parameters to the appropriate banking partner. --- ## Fiat wallet setup Virtual accounts connect to fiat wallets, which store your funds. Request wallet activation from your Account Manager—fiat wallets require verification before use. Specify the currency (EUR, USD, GBP) and payment method based on your use case: - **EUR**: SEPA payments for EEA transactions - **USD**: ACH, FedWire, or SWIFT for US and international payments - **GBP**: Faster Payments for UK domestic transactions Each wallet receives a virtual account with a unique vIBAN. Named accounts are issued in your business name or your customers' names for personalized payment routing. After you have created the wallet, find its ID in the Wallet section on the Portal. You will later need it for creating payments. ![WalletId](/img/bvnk/use-cases/walletId.png) --- --- **What's next?** Once you have completed all prerequisites above, you can proceed with [initiating fiat payouts](../initiate-payment-2). --- ## Refund payments(Virtual-accounts) 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-transaction-report) 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-transaction-report) 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: ```json { "value": "150", "currency": "EUR" } ``` | Parameter | Required | Description | | :--------- | :------: | :------------------------------------------------------------ | | `value` | :white_check_mark: | Amount to refund | | `currency` | :white_check_mark: | Currency of the refund pay-in. Possible values: GBP, USD, 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. * `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: | Parameter | Required | Description | | :-------- | :------: | :---------- | | `X-Idempotency-Key` | :white_check_mark: | Idempotency key in the UUID format used to guarantee that retrying the same request will not create duplicate refunds. | | `paymentReference` | :white_check_mark: | Additional information about the payment that can be sent to processing partners. Can be any text | | `amount` | :white_check_mark: | Amount to refund. Includes parameters:* `value`: amount of the refund.* `currency`: Currency of the refund pay-in. Possible values: * GBP * USD * EUR | 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). --- ## Receive, send, and store funds with Virtual Accounts The following guide explains how to use virtual accounts (VA) to manage fiat and crypto payments via BVNK. A **virtual account** (VA) is a BVNK-managed payment account for sending, holding, and receiving both fiat and crypto funds in one place. The **customer virtual accounts** product allows you to create virtual accounts for your customers, simplifying how you track and manage their payments. To handle 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 virtual IBAN (vIBAN). These vIBANs link to your payment account so you can send and receive payments on behalf of your customers under their names. BVNK offers virtual accounts as an optional solution. Partners and customers may use them, but they are not required for all. You can use other BVNK services (such as payments, crypto trading, or treasury management) without setting up or managing virtual accounts. To qualify for customer virtual accounts, a partner must: - Be a fully licensed financial services provider (e.g., UK FCA license, Canadian MSB, etc.). - Demonstrate a robust financial crime control framework (AML, risk policies, transaction monitoring, etc.). - Undergo compliance checks, including documentation review and interviews with the BVNK's compliance team. --- ## For who is this product intended? Virtual accounts are designed for regulated Financial Institutions and similar businesses that need to manage payments for themselves and their customers. We simplify the entire process and offer tools to efficiently manage funds between your business, your customers, and their end-customers. The product supports two main models: - **Principal virtual accounts** Principal virtual accounts are created for direct BVNK customers, including but not limited to: - **Any business entity**: Fintech companies, gaming platforms, crypto businesses, and more - **Physical persons**: In the embedded model, individual persons can also have virtual accounts - **Customer virtual accounts** Customer virtual accounts enable financial institutions and banks to create underlying virtual accounts for their own customers, such as: - **Regulated Financial Institutions** - **Banks** - **Payment Service Providers (PSPs)** - **Crypto and Trading Platforms** --- ## What is a standard use case for virtual accounts? VAs are intended for partners who need to segregate funds, simplify reconciliation, or embed financial services in their platforms. Virtual accounts enable various 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 principal wallet, and you can automatically credit the customer's trading account balance. Your customers can also convert their fiat deposits to crypto, trade, and then withdraw crypto or convert back to fiat—all through their virtual account. > > This setup enables seamless fund segregation, accurate reconciliation, and a smooth user experience for both fiat and crypto operations. **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 their 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. The flow can optionally include stablecoin pre-funding for faster settlement and cross-border capabilities. **Additional use cases**: Virtual accounts support various other scenarios: - **Neobanks**: Issue named IBANs to your customers for personal or business banking services - **Marketplaces**: Provide each merchant with their own virtual account to receive payments from buyers - **Employer of Record services**: Create virtual accounts for each client company to manage payroll and contractor payments - **Treasury management**: Segregate funds across different business units or projects with dedicated virtual accounts --- ## How virtual accounts work? **Virtual accounts** are connected to wallets, which serve as the storage of value with balances. A **wallet** is a storage of value that maintains balances. It can hold fiat or crypto value. Various payment instruments can be connected to a wallet, including Crypto addresses (for crypto). If crypto lands on a channel connected to a fiat wallet, it's automatically converted and deposited in the wallet as fiat. If a virtual account is linked to that wallet, you can withdraw funds through the VA. The storage of value is separate from the payment instrument linked to it so that you can manage funds flexibly. Financial institutions can have the **Principal Wallet**, which is the storage where the financial institution stores its value. It works like any other fiat wallet. With it, the institution can: - Create underlying virtual accounts for their customers - Create wallets for their customers - Manage funds across the entire structure --- ## Supported virtual account types BVNK supports various virtual account types to meet different payment needs: - **Named Accounts** are issued in the name of your business or your customers, enabling personalized payment routing. They are dedicated to domestic payments (within the EEA region) and support the following payment methods and currencies: - **USD**: ACH and other domestic payment methods - **EUR**: SEPA payments - **GBP**: Faster Payments - **Pulled accounts** are standard SWIFT accounts with Virtual International Bank Account Numbers (vIBANs) used for cross-border payments. They are issued in the name of a BVNK-owned legal entity, "System Payed Services Malta". --- ## How to get started? Before you can request or create virtual accounts, your business must be approved for the customer virtual accounts solution. To request virtual accounts, do the following: 1. Contact BVNK (via your account manager or sales contact) to express interest in the VA product. 2. Submit required documentation: - Proof of regulatory status (e.g., 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 occur, especially if your business is in a higher-risk sector (for example, gambling, gaming, or trading). After BVNK's compliance team approves your application: 1. The **commercial team** or support success team creates the VA product configuration, specifying which currency virtual accounts you need (GBP, USD, EUR) and other parameters. 2. The **account activation** team maps your parameters to the appropriate banking partner. 3. A wallet is created in the admin panel, mapped to the banking partner. 4. Once the wallet is created, a virtual account (USD/GBP/EUR VA or SWIFT account) is attached to it. Once a virtual account is created, you can immediately: - Receive funds directly into it. - Send payments from the virtual account. - Create, open, close, and block wallets for yourself and your customers as needed. - Convert between fiat and crypto when you have the appropriate channels connected. Also, after your business is enabled for VAs, you can create VAs for your underlying customers. For that: - Collect KYC information for your customer (the end user who will receive the VA). - Screen the customer according to BVNK's requirements (manual screening, risk checks). --- **Ready to start?** Once you have completed the setup process with BVNK, you can proceed with: - [Managing wallets](../../../get-started/creating-your-first-wallet) - [Understanding supported currencies](../../../references/currencies) - [Creating internal transfers](../../create-internal-transfer) - [Initiating payouts](../initiate-payment-2) --- ## 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.