Prepare a pay-out route ownership proof
curl --request POST \
--url https://api.venlyfinance.com/v1/accounts/{accountId}/payout-routes/{routeId}/ownership-proof/prepare \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.venlyfinance.com/v1/accounts/{accountId}/payout-routes/{routeId}/ownership-proof/prepare"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.venlyfinance.com/v1/accounts/{accountId}/payout-routes/{routeId}/ownership-proof/prepare', 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}/payout-routes/{routeId}/ownership-proof/prepare",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.venlyfinance.com/v1/accounts/{accountId}/payout-routes/{routeId}/ownership-proof/prepare"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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}/payout-routes/{routeId}/ownership-proof/prepare")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.venlyfinance.com/v1/accounts/{accountId}/payout-routes/{routeId}/ownership-proof/prepare")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"success": true,
"result": {
"walletAddress": "0x7c6b9095Ae1E0F1D0b1B9c8A5f4E3d2C1b0A9f8E",
"blockchain": "BASE",
"message": "I confirm ownership of wallet 0x7c6b9095... on 20/08/2026",
"signedOnUtc": "2026-08-20"
}
}{
"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": "account-not-found",
"message": "The requested resource was not found."
}
]
}{
"success": false,
"errors": [
{
"code": "internal-error",
"message": "An unexpected error occurred. Please try again later."
}
]
}Payout Routes
Prepare a route ownership proof
Get the exact message a self-custody wallet owner must sign to finish registering a pay-out route.
POST
/
accounts
/
{accountId}
/
payout-routes
/
{routeId}
/
ownership-proof
/
prepare
Prepare a pay-out route ownership proof
curl --request POST \
--url https://api.venlyfinance.com/v1/accounts/{accountId}/payout-routes/{routeId}/ownership-proof/prepare \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.venlyfinance.com/v1/accounts/{accountId}/payout-routes/{routeId}/ownership-proof/prepare"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.venlyfinance.com/v1/accounts/{accountId}/payout-routes/{routeId}/ownership-proof/prepare', 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}/payout-routes/{routeId}/ownership-proof/prepare",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.venlyfinance.com/v1/accounts/{accountId}/payout-routes/{routeId}/ownership-proof/prepare"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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}/payout-routes/{routeId}/ownership-proof/prepare")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.venlyfinance.com/v1/accounts/{accountId}/payout-routes/{routeId}/ownership-proof/prepare")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"success": true,
"result": {
"walletAddress": "0x7c6b9095Ae1E0F1D0b1B9c8A5f4E3d2C1b0A9f8E",
"blockchain": "BASE",
"message": "I confirm ownership of wallet 0x7c6b9095... on 20/08/2026",
"signedOnUtc": "2026-08-20"
}
}{
"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": "account-not-found",
"message": "The requested resource was not found."
}
]
}{
"success": false,
"errors": [
{
"code": "internal-error",
"message": "An unexpected error occurred. Please try again later."
}
]
}Requires scope:
The message is date-bound (
manage:pay-outs — see Required scopes.
Returns the exact text the owner of a self-custody wallet must sign to prove they control it, so the pay-out route can finish registering.
A route parks at AWAITING_OWNERSHIP_PROOF when its destination requires a Travel-Rule ownership proof and the source wallet is self-custody. Venly holds no key for that wallet, so only its owner can sign.
This step is stateless — nothing is stored, and there’s no id or expiry. Calling it twice for the same route returns an equivalent message, so it’s safe to repeat.
The owner signs
message verbatim. Don’t reformat, re-encode, pretty-print or reconstruct it — the signature is checked against those exact bytes.Only an externally-owned account can sign: the scheme is EIP-191, and ERC-1271 contract signatures are not supported. A smart-contract wallet cannot complete this step.signedOnUtc), so a proof prepared on one day can’t be submitted against a message built for another. Sign and submit in the same session.
Submit the result with Submit a route ownership proof. See Pay-outs.Authorizations
OAuth2 client credentials flow. Token endpoints:
Path Parameters
Unique account identifier
Unique pay-out route identifier
Was this page helpful?

