curl --request POST \
--url https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "EUR Payouts",
"inCurrency": "EUR",
"targetCryptocurrency": "USDC",
"idempotencyKey": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
'import requests
url = "https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts"
payload = {
"name": "EUR Payouts",
"inCurrency": "EUR",
"targetCryptocurrency": "USDC",
"idempotencyKey": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'EUR Payouts',
inCurrency: 'EUR',
targetCryptocurrency: 'USDC',
idempotencyKey: '3fa85f64-5717-4562-b3fc-2c963f66afa6'
})
};
fetch('https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'EUR Payouts',
'inCurrency' => 'EUR',
'targetCryptocurrency' => 'USDC',
'idempotencyKey' => '3fa85f64-5717-4562-b3fc-2c963f66afa6'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts"
payload := strings.NewReader("{\n \"name\": \"EUR Payouts\",\n \"inCurrency\": \"EUR\",\n \"targetCryptocurrency\": \"USDC\",\n \"idempotencyKey\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"EUR Payouts\",\n \"inCurrency\": \"EUR\",\n \"targetCryptocurrency\": \"USDC\",\n \"idempotencyKey\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"EUR Payouts\",\n \"inCurrency\": \"EUR\",\n \"targetCryptocurrency\": \"USDC\",\n \"idempotencyKey\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"result": {
"id": "4d5e6f70-8192-4a3b-9c4d-5e6f7081920a",
"accountId": "b2a1f0e9-8c7d-4e3a-9f21-0a1b2c3d4e5f",
"bankAccountType": "EUR_SEPA",
"name": "EUR Payouts",
"status": "ACTIVE",
"currency": "EUR",
"targetCryptocurrency": "USDC",
"iban": "DE89370400440532013000",
"bic": "DEUTDEDB",
"bankName": "Example Bank",
"beneficiaryName": "Jane Doe",
"referenceCode": "VFY-7K2Q-931",
"depositRails": [
{
"railType": "SEPA",
"iban": "DE89370400440532013000",
"bic": "DEUTDEDB",
"bankName": "Example Bank",
"beneficiaryName": "Jane Doe",
"paymentReference": "VFY-7K2Q-931"
}
],
"createdAt": "2026-01-15T09:30:00Z",
"updatedAt": "2026-01-15T09:30:00Z"
}
}{
"success": false,
"errors": [
{
"code": "invalid-request",
"message": "The request contains invalid parameters."
}
],
"result": {}
}{
"success": false,
"errors": [
{
"code": "unauthenticated",
"message": "Please authenticate to perform this action."
}
]
}{
"success": false,
"errors": [
{
"code": "forbidden",
"message": "You do not have permission to access this resource."
}
]
}{
"success": false,
"errors": [
{
"code": "invalid-request",
"message": "The request contains invalid parameters."
}
],
"result": {}
}{
"success": false,
"errors": [
{
"code": "invalid-request",
"message": "The request contains invalid parameters."
}
],
"result": {}
}{
"success": false,
"errors": [
{
"code": "invalid-request",
"message": "The request contains invalid parameters."
}
],
"result": {}
}{
"success": false,
"errors": [
{
"code": "invalid-request",
"message": "The request contains invalid parameters."
}
],
"result": {}
}{
"success": false,
"errors": [
{
"code": "invalid-request",
"message": "The request contains invalid parameters."
}
],
"result": {}
}Create a virtual bank account
Assign a virtual IBAN to an account for fiat pay-ins.
curl --request POST \
--url https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "EUR Payouts",
"inCurrency": "EUR",
"targetCryptocurrency": "USDC",
"idempotencyKey": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
'import requests
url = "https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts"
payload = {
"name": "EUR Payouts",
"inCurrency": "EUR",
"targetCryptocurrency": "USDC",
"idempotencyKey": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'EUR Payouts',
inCurrency: 'EUR',
targetCryptocurrency: 'USDC',
idempotencyKey: '3fa85f64-5717-4562-b3fc-2c963f66afa6'
})
};
fetch('https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'EUR Payouts',
'inCurrency' => 'EUR',
'targetCryptocurrency' => 'USDC',
'idempotencyKey' => '3fa85f64-5717-4562-b3fc-2c963f66afa6'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts"
payload := strings.NewReader("{\n \"name\": \"EUR Payouts\",\n \"inCurrency\": \"EUR\",\n \"targetCryptocurrency\": \"USDC\",\n \"idempotencyKey\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"EUR Payouts\",\n \"inCurrency\": \"EUR\",\n \"targetCryptocurrency\": \"USDC\",\n \"idempotencyKey\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.venlyfinance.com/v1/accounts/{accountId}/virtual-bank-accounts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"EUR Payouts\",\n \"inCurrency\": \"EUR\",\n \"targetCryptocurrency\": \"USDC\",\n \"idempotencyKey\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"result": {
"id": "4d5e6f70-8192-4a3b-9c4d-5e6f7081920a",
"accountId": "b2a1f0e9-8c7d-4e3a-9f21-0a1b2c3d4e5f",
"bankAccountType": "EUR_SEPA",
"name": "EUR Payouts",
"status": "ACTIVE",
"currency": "EUR",
"targetCryptocurrency": "USDC",
"iban": "DE89370400440532013000",
"bic": "DEUTDEDB",
"bankName": "Example Bank",
"beneficiaryName": "Jane Doe",
"referenceCode": "VFY-7K2Q-931",
"depositRails": [
{
"railType": "SEPA",
"iban": "DE89370400440532013000",
"bic": "DEUTDEDB",
"bankName": "Example Bank",
"beneficiaryName": "Jane Doe",
"paymentReference": "VFY-7K2Q-931"
}
],
"createdAt": "2026-01-15T09:30:00Z",
"updatedAt": "2026-01-15T09:30:00Z"
}
}{
"success": false,
"errors": [
{
"code": "invalid-request",
"message": "The request contains invalid parameters."
}
],
"result": {}
}{
"success": false,
"errors": [
{
"code": "unauthenticated",
"message": "Please authenticate to perform this action."
}
]
}{
"success": false,
"errors": [
{
"code": "forbidden",
"message": "You do not have permission to access this resource."
}
]
}{
"success": false,
"errors": [
{
"code": "invalid-request",
"message": "The request contains invalid parameters."
}
],
"result": {}
}{
"success": false,
"errors": [
{
"code": "invalid-request",
"message": "The request contains invalid parameters."
}
],
"result": {}
}{
"success": false,
"errors": [
{
"code": "invalid-request",
"message": "The request contains invalid parameters."
}
],
"result": {}
}{
"success": false,
"errors": [
{
"code": "invalid-request",
"message": "The request contains invalid parameters."
}
],
"result": {}
}{
"success": false,
"errors": [
{
"code": "invalid-request",
"message": "The request contains invalid parameters."
}
],
"result": {}
}manage:accounts — see Required scopes.
Provisions a virtual bank account. Provide a display name, the fiat inCurrency to receive (EUR provisions a EUR_SEPA IBAN), the targetCryptocurrency incoming funds convert to, and a unique idempotencyKey (safe retries). The response includes the IBAN/BIC and a referenceCode the payer must quote so the wire is matched to this account.
Errors
The codes this endpoint can return, in addition to the standard errors. Branch oncode, never the message.
| HTTP | code | When |
|---|---|---|
400 | duplicate-virtual-bank-account | an active account already exists for this wallet + currency pair |
400 | tenant-not-onboarded-for-lane | the requested lane is not enabled for your tenant |
400 | tenant-kyb-not-approved | your tenant’s recorded KYB status is not approved |
400 | unsupported-asset | targetCryptocurrency is not a supported asset for your company |
400 | no-suitable-provider | no enabled provider supports the requested currency pair |
400 | account-holder-unresolved | the account does not resolve to exactly one active account-holder party |
400 | ownership-proof-required | this account requires a signed wallet-ownership proof, but no ownershipProof block was supplied |
400 | invalid-proof-message | ownershipProof.message is malformed or does not reference the wallet address and embedded token |
400 | signature-mismatch | ownershipProof.signature does not recover the supplied wallet address |
400 | validation-error | the provider rejected the request as invalid |
404 | account-not-found | no such account for your company |
404 | wallet-not-found | the ownershipProof wallet address/blockchain is not this account’s self-custody wallet on that chain |
409 | idempotency-conflict | a concurrent request with the same idempotencyKey is still processing |
409 | recipient-verification-pending | the account holder’s verification is still pending. Retryable once approved |
422 | idempotency-conflict | the key was reused with a different body, or replays a failed attempt (use a new key) |
422 | ambiguous-lane | multiple providers match the currency pair and no lane preference is configured to break the tie |
422 | recipient-verification-rejected | the account holder’s verification was rejected or revoked; provisioning cannot proceed |
Authorizations
OAuth2 client credentials flow. Token endpoints:
Path Parameters
Unique account identifier
Body
Request to create a virtual bank account. The rail is resolved from inCurrency and your
tenant's enabled pay-in configuration (EUR → SEPA, USD → ACH).
Display name for the bank account
255Fiat currency to receive (ISO 4217). Determines the bank account type (EUR -> EUR_SEPA)
^[A-Z]{3}$Cryptocurrency to convert incoming fiat payments to (e.g. USDC)
^[A-Za-z0-9]{2,10}$Unique key to prevent duplicate operations on retry
255Customer-signed wallet-ownership proof. Required where the virtual bank account settles to a self-custody account wallet that must prove control first — the destination is the customer's own wallet, so Venly cannot sign the proof itself.
Obtain message from
prepare,
have the customer sign it verbatim with the destination wallet, and submit it here. Ignored
where no proof is required, so it is safe to send unconditionally.
Show child attributes
Show child attributes
Response
Virtual bank account created
Indicates whether the request was successful
Virtual bank account for receiving fiat payments that are auto-converted to crypto.
EUR_SEPA accounts return iban/bic; USD_ACH accounts return
accountNumber/routingNumber. Read depositRails for the complete instruction set
per rail — the summary fields below always describe the primary rail.
Show child attributes
Show child attributes
Was this page helpful?

