curl --request POST \
--url https://api.rolla.xyz/api/v1/external/wallet/withdraw \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"amount": 5000,
"currency": "NGN",
"description": "Vendor payment",
"inlineBeneficiary": {
"account_number": "0123456789",
"bank_name": "Access Bank",
"bank_code": "000014"
},
"saveBeneficiary": true
}
'import requests
url = "https://api.rolla.xyz/api/v1/external/wallet/withdraw"
payload = {
"amount": 5000,
"currency": "NGN",
"description": "Vendor payment",
"inlineBeneficiary": {
"account_number": "0123456789",
"bank_name": "Access Bank",
"bank_code": "000014"
},
"saveBeneficiary": True
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
amount: 5000,
currency: 'NGN',
description: 'Vendor payment',
inlineBeneficiary: {account_number: '0123456789', bank_name: 'Access Bank', bank_code: '000014'},
saveBeneficiary: true
})
};
fetch('https://api.rolla.xyz/api/v1/external/wallet/withdraw', 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.rolla.xyz/api/v1/external/wallet/withdraw",
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([
'amount' => 5000,
'currency' => 'NGN',
'description' => 'Vendor payment',
'inlineBeneficiary' => [
'account_number' => '0123456789',
'bank_name' => 'Access Bank',
'bank_code' => '000014'
],
'saveBeneficiary' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$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.rolla.xyz/api/v1/external/wallet/withdraw"
payload := strings.NewReader("{\n \"amount\": 5000,\n \"currency\": \"NGN\",\n \"description\": \"Vendor payment\",\n \"inlineBeneficiary\": {\n \"account_number\": \"0123456789\",\n \"bank_name\": \"Access Bank\",\n \"bank_code\": \"000014\"\n },\n \"saveBeneficiary\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
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.rolla.xyz/api/v1/external/wallet/withdraw")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 5000,\n \"currency\": \"NGN\",\n \"description\": \"Vendor payment\",\n \"inlineBeneficiary\": {\n \"account_number\": \"0123456789\",\n \"bank_name\": \"Access Bank\",\n \"bank_code\": \"000014\"\n },\n \"saveBeneficiary\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.rolla.xyz/api/v1/external/wallet/withdraw")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 5000,\n \"currency\": \"NGN\",\n \"description\": \"Vendor payment\",\n \"inlineBeneficiary\": {\n \"account_number\": \"0123456789\",\n \"bank_name\": \"Access Bank\",\n \"bank_code\": \"000014\"\n },\n \"saveBeneficiary\": true\n}"
response = http.request(request)
puts response.read_body{
"status": 200,
"message": "Withdrawal initiated successfully",
"success": true,
"data": {
"transaction": {
"id": "866b7abd-6cac-40f2-a04f-d6e58bf47d04",
"transaction_type": "withdrawal",
"status": "pending",
"description": "Vendor payment",
"external_reference": "NG-NFNUJTUW",
"fee_amount": 25,
"source_amount": 5025,
"destination_amount": 5000,
"source_currency": "NGN",
"destination_currency": "NGN",
"created_at": "2026-03-23T13:45:28.138Z",
"updated_at": "2026-03-23T13:45:28.263Z",
"beneficiary": {
"bank_name": "Access Bank",
"bank_code": "000014",
"account_name": "John Doe",
"account_number": "0123456789"
}
}
}
}{
"status": 400,
"message": "Validation failed",
"errors": [
{
"path": [
"inlineBeneficiary",
"routing_number"
],
"message": "routing_number is required for ACH and domestic wire withdrawals"
}
]
}{
"status": 400,
"message": "Validation failed",
"code": "ACCOUNT_RESTRICTED",
"errors": [
{
"path": [
"amount"
],
"message": "amount must be a whole number of smallest currency units (e.g. cents)"
}
]
}{
"status": 403,
"message": "This endpoint requires IP whitelisting. Add at least one whitelisted IP to your API key before using withdraw."
}Withdraw Funds
Initiates a withdrawal from your wallet to a beneficiary. Either beneficiaryId or inlineBeneficiary must be provided. Requires an API key with at least one whitelisted IP. For USD international_wire, send as multipart/form-data and attach reference documents under the documents field.
curl --request POST \
--url https://api.rolla.xyz/api/v1/external/wallet/withdraw \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"amount": 5000,
"currency": "NGN",
"description": "Vendor payment",
"inlineBeneficiary": {
"account_number": "0123456789",
"bank_name": "Access Bank",
"bank_code": "000014"
},
"saveBeneficiary": true
}
'import requests
url = "https://api.rolla.xyz/api/v1/external/wallet/withdraw"
payload = {
"amount": 5000,
"currency": "NGN",
"description": "Vendor payment",
"inlineBeneficiary": {
"account_number": "0123456789",
"bank_name": "Access Bank",
"bank_code": "000014"
},
"saveBeneficiary": True
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
amount: 5000,
currency: 'NGN',
description: 'Vendor payment',
inlineBeneficiary: {account_number: '0123456789', bank_name: 'Access Bank', bank_code: '000014'},
saveBeneficiary: true
})
};
fetch('https://api.rolla.xyz/api/v1/external/wallet/withdraw', 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.rolla.xyz/api/v1/external/wallet/withdraw",
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([
'amount' => 5000,
'currency' => 'NGN',
'description' => 'Vendor payment',
'inlineBeneficiary' => [
'account_number' => '0123456789',
'bank_name' => 'Access Bank',
'bank_code' => '000014'
],
'saveBeneficiary' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$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.rolla.xyz/api/v1/external/wallet/withdraw"
payload := strings.NewReader("{\n \"amount\": 5000,\n \"currency\": \"NGN\",\n \"description\": \"Vendor payment\",\n \"inlineBeneficiary\": {\n \"account_number\": \"0123456789\",\n \"bank_name\": \"Access Bank\",\n \"bank_code\": \"000014\"\n },\n \"saveBeneficiary\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
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.rolla.xyz/api/v1/external/wallet/withdraw")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 5000,\n \"currency\": \"NGN\",\n \"description\": \"Vendor payment\",\n \"inlineBeneficiary\": {\n \"account_number\": \"0123456789\",\n \"bank_name\": \"Access Bank\",\n \"bank_code\": \"000014\"\n },\n \"saveBeneficiary\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.rolla.xyz/api/v1/external/wallet/withdraw")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 5000,\n \"currency\": \"NGN\",\n \"description\": \"Vendor payment\",\n \"inlineBeneficiary\": {\n \"account_number\": \"0123456789\",\n \"bank_name\": \"Access Bank\",\n \"bank_code\": \"000014\"\n },\n \"saveBeneficiary\": true\n}"
response = http.request(request)
puts response.read_body{
"status": 200,
"message": "Withdrawal initiated successfully",
"success": true,
"data": {
"transaction": {
"id": "866b7abd-6cac-40f2-a04f-d6e58bf47d04",
"transaction_type": "withdrawal",
"status": "pending",
"description": "Vendor payment",
"external_reference": "NG-NFNUJTUW",
"fee_amount": 25,
"source_amount": 5025,
"destination_amount": 5000,
"source_currency": "NGN",
"destination_currency": "NGN",
"created_at": "2026-03-23T13:45:28.138Z",
"updated_at": "2026-03-23T13:45:28.263Z",
"beneficiary": {
"bank_name": "Access Bank",
"bank_code": "000014",
"account_name": "John Doe",
"account_number": "0123456789"
}
}
}
}{
"status": 400,
"message": "Validation failed",
"errors": [
{
"path": [
"inlineBeneficiary",
"routing_number"
],
"message": "routing_number is required for ACH and domestic wire withdrawals"
}
]
}{
"status": 400,
"message": "Validation failed",
"code": "ACCOUNT_RESTRICTED",
"errors": [
{
"path": [
"amount"
],
"message": "amount must be a whole number of smallest currency units (e.g. cents)"
}
]
}{
"status": 403,
"message": "This endpoint requires IP whitelisting. Add at least one whitelisted IP to your API key before using withdraw."
}beneficiaryId or inlineBeneficiary must be provided. The required fields inside inlineBeneficiary depend on the currency and withdrawal_method.
X-Account-Id header set to the merchant’s account ID — the payout is then deducted from that merchant’s wallet. Without it, the withdrawal runs against your own (parent) account. The account ID is the id returned by POST /accounts (or from GET /accounts).amount is US cents — e.g. 100 cents = $1.00 USD transferred (consistent with ledger *_amount fields returned on transactions).403 and the message "This endpoint requires IP whitelisting. Add at least one whitelisted IP to your API key before using withdraw."Top-level Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Amount in the smallest unit (kobo for NGN, cents for USD — 100 cents = $1.00 USD). Must be a whole number of smallest units and at least 1 |
currency | string | Yes | 3-letter currency code — NGN, USD, or XAF |
description | string | Yes | Narration for the transaction, 1–500 characters |
externalReference | string | No | Your own unique reference for this transaction, up to 255 characters. Used on USD and other non-NGN bank rails. NGN and Mobile Money payouts always get a Rolla-generated reference instead (for example NG-NFNUJTUW), because the provider imposes its own format |
beneficiaryId | string (UUID) | No* | ID of a saved beneficiary |
inlineBeneficiary | object | No* | One-time beneficiary details (see per-currency fields below) |
saveBeneficiary | boolean | No | Save the inline beneficiary for future use |
deductFeesFromBalance | boolean | No | Defaults to true on this API: the fee is taken from your wallet balance on top of amount, and the beneficiary receives the exact amount. Set false to take the fee out of the amount instead, so the beneficiary receives less |
metadata | object | No | Your own key/value data, stored on the transaction and returned on every response and webhook for it. See Attaching your own metadata |
payerNameId | string (UUID) | No | Which of your registered payer names the beneficiary should see on this payout. Requires Named Payouts to be enabled. See Choosing the payer name |
beneficiaryId or inlineBeneficiary is required.
inlineBeneficiary — Required Fields by Currency
NGN
| Field | Required |
|---|---|
account_number | Yes |
bank_name | Yes |
bank_code | Yes |
account_name | No (auto-resolved from bank lookup) |
curl -X POST "https://api.rolla.xyz/api/v1/external/wallet/withdraw" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"currency": "NGN",
"description": "Vendor payment",
"inlineBeneficiary": {
"account_number": "0123456789",
"bank_name": "Access Bank",
"bank_code": "000014"
},
"saveBeneficiary": true
}'
Choosing a USD rail
USD payouts run on one of three bank rails, selected withwithdrawal_method. The choice drives settlement speed, the fields you must supply, and the fee — so pass it explicitly on both the quote and the withdrawal.
withdrawal_method | Use for | Addressed by | Notes |
|---|---|---|---|
ach | Domestic US, cost-sensitive | routing_number (ABA) | Settles in business days. Cheapest US rail. |
domestic_wire | Domestic US, time-sensitive | routing_number (ABA) | Same-day settlement, priced above ACH. |
international_wire | Outside the US | swift_code | Requires supporting documents — multipart/form-data only. |
ach and domestic_wire to the same bank generally cost different amounts. Send withdrawal_method (or beneficiaryId) to POST /wallet/fee to quote the exact fee this withdrawal will be charged.USD — ACH
| Field | Required |
|---|---|
withdrawal_method | Yes — "ach" |
account_name | Yes |
bank_name | Yes |
account_number | Yes |
routing_number | Yes — 9-digit ABA number |
beneficiary_address | Yes — { street, city, state, postalCode, country } |
bank_address | Yes — { street, city, state, postalCode, country } |
account_category | No — "checking" or "savings". Recommended; carried onto the saved record when saveBeneficiary: true |
curl -X POST "https://api.rolla.xyz/api/v1/external/wallet/withdraw" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"amount": 1000,
"currency": "USD",
"description": "Payroll run",
"inlineBeneficiary": {
"withdrawal_method": "ach",
"account_name": "Jane Smith",
"bank_name": "Chase Bank",
"account_number": "123456789",
"routing_number": "021000021",
"account_category": "checking",
"beneficiary_address": { "street": "123 Main St", "city": "New York", "state": "NY", "postalCode": "10001", "country": "US" },
"bank_address": { "street": "270 Park Ave", "city": "New York", "state": "NY", "postalCode": "10017", "country": "US" }
}
}'
international_wire, ACH needs no supporting documents — send it as a normal JSON body.routing_number is required for ach and domestic_wire. Omitting it returns a 400 with "routing_number is required for ACH and domestic wire withdrawals".This is newly enforced. It has always been documented as required for domestic_wire, but the server previously accepted domestic wire requests without it and the payout then stranded. If you have been omitting it, those requests now fail fast instead.USD — Domestic Wire
| Field | Required |
|---|---|
withdrawal_method | Yes — "domestic_wire" |
account_name | Yes |
bank_name | Yes |
account_number | Yes |
routing_number | Yes |
beneficiary_address | Yes — { street, city, state, postalCode, country } |
bank_address | Yes — { street, city, state, postalCode, country } |
curl -X POST "https://api.rolla.xyz/api/v1/external/wallet/withdraw" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"amount": 1000,
"currency": "USD",
"description": "Supplier settlement",
"inlineBeneficiary": {
"withdrawal_method": "domestic_wire",
"account_name": "Jane Smith",
"bank_name": "Chase Bank",
"account_number": "123456789",
"routing_number": "021000021",
"beneficiary_address": { "street": "123 Main St", "city": "New York", "state": "NY", "postalCode": "10001", "country": "US" },
"bank_address": { "street": "270 Park Ave", "city": "New York", "state": "NY", "postalCode": "10017", "country": "US" }
}
}'
USD — International Wire
| Field | Required |
|---|---|
withdrawal_method | Yes — "international_wire" |
account_name | Yes |
bank_name | Yes |
account_number | Yes |
swift_code | Yes |
beneficiary_address | Yes — { street, city, state, postalCode, country } |
bank_address | Yes — { street, city, state, postalCode, country } |
intermediary_bank_name | No |
intermediary_bank_routing_number | No |
multipart/form-data (every other currency/method uses a JSON body). Include one or more files under the documents field.inlineBeneficiary as a single JSON-stringified field — one form field named inlineBeneficiary whose value is the full JSON object. Do not spread it across bracketed fields like inlineBeneficiary[account_name]=…; multipart form fields are not reconstructed into a nested object, so the request will fail with 400 "Either beneficiaryId or inlineBeneficiary must be provided".curl -X POST "https://api.rolla.xyz/api/v1/external/wallet/withdraw" \
-H "X-API-Key: your_api_key_here" \
-F "amount=1000" \
-F "currency=USD" \
-F "description=Invoice 2291" \
-F 'inlineBeneficiary={
"withdrawal_method": "international_wire",
"account_name": "Jane Smith",
"bank_name": "Barclays Bank",
"account_number": "12345678",
"swift_code": "BARCGB22",
"beneficiary_address": { "street": "123 Main Street", "city": "New York", "state": "NY", "postalCode": "10001", "country": "US" },
"bank_address": { "street": "456 Bank Avenue", "city": "New York", "state": "NY", "postalCode": "10001", "country": "US" }
}' \
-F "documents=@/path/to/invoice.pdf"
USD — Crypto (USDT / USDC)
Crypto withdrawals are sent from your USD wallet. Setwithdrawal_method to "crypto_usdt" or "crypto_usdc" to send the USD value as a stablecoin to a crypto wallet address.
| Field | Required |
|---|---|
withdrawal_method | Yes — "crypto_usdt" or "crypto_usdc" |
account_name | Yes — used as a display nickname |
wallet_address | Yes — 26–64 alphanumeric characters |
wallet_chain | Yes — e.g. "ethereum", "tron" |
curl -X POST "https://api.rolla.xyz/api/v1/external/wallet/withdraw" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"amount": 100,
"currency": "USD",
"description": "Treasury top-up",
"inlineBeneficiary": {
"withdrawal_method": "crypto_usdt",
"account_name": "My USDT Wallet",
"wallet_address": "TXyZ1234abcd5678efgh9012ijkl3456mnop",
"wallet_chain": "tron"
}
}'
XAF
| Field | Required |
|---|---|
account_name | Yes |
bank_name | Yes |
account_number | Yes |
swift_code | Yes |
bank_address | Yes — { street, city, state, country } |
Choosing the payer name
A payout reaches the beneficiary under your own business name by default. If Named Payouts is enabled for your account, passpayerNameId to send it
under one of your registered affiliated payer names instead — a related entity of yours, or the
end-customer you are paying on behalf of.
payerNameId is accepted on USD international wire only. Attaching one to any other rail —
ACH, domestic wire, crypto, NGN, XAF, mobile money — is refused with a 400:A payer name can only be used on an International Wire (SWIFT) payout. Remove the payer name,
or send this payment to a beneficiary whose withdrawal method is international_wire.
multipart/form-data, a payout
carrying payerNameId is always a multipart request — never a JSON body.curl -X POST "https://api.rolla.xyz/api/v1/external/wallet/withdraw" \
-H "X-API-Key: your_api_key_here" \
-F "amount=250000" \
-F "currency=USD" \
-F "description=Invoice 2291" \
-F "beneficiaryId=c3d4e5f6-a7b8-9012-cdef-123456789012" \
-F "payerNameId=9f8e7d6c-5b4a-4938-8271-0a1b2c3d4e5f" \
-F "documents=@/path/to/invoice.pdf"
beneficiaryId must itself be an international_wire beneficiary;
the rail is read from the beneficiary when you do not send inlineBeneficiary.
Omit payerNameId and the payout uses your default payer name if you have set one, and your own business name if you have not.
The id must be a payer name on your own account, and it must not be rejected or archived. Every one
of those is refused here with a 400 and no payout is created: a rejected or archived name with
"That payer name was rejected and cannot be used" / "That payer name has been archived and can no longer be used", and an id that does not exist or belongs to another account with
"Payer name not found". Note that this endpoint answers 400 for the unknown id too, rather than
404: a payer name is scoped to your own account, so an id you cannot use simply reads as an
unusable name on the payout you tried to create. A name that has not finished
registration yet is accepted, but the payout waits until the name is usable before the money
moves, so send time-sensitive payments only under a name reported as active. The
Named Payouts guide covers the statuses and the webhook that tells you
when a name is ready.
Attaching your own metadata
Pass ametadata object to carry your own identifiers on the payout — an order id, a
ledger key, whatever you reconcile against. Rolla stores it verbatim and never interprets
it, and returns it unchanged on every response and webhook for that transaction, so you
can match our record to yours without keeping a mapping table.
curl -X POST "https://api.rolla.xyz/api/v1/external/wallet/withdraw" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"currency": "NGN",
"description": "Vendor payment",
"beneficiaryId": "79ee09c0-1518-4a5c-86c7-d880a78ec6e5",
"metadata": {
"invoice_id": "INV-2026-042",
"cost_centre": "AP-LAGOS",
"retry": false
}
}'
"metadata": { "invoice_id": "INV-2026-042", "cost_centre": "AP-LAGOS", "retry": false }
| Rule | Limit |
|---|---|
| Keys | At most 20 |
| Key length | 64 characters |
| Value types | string, number, boolean, or null — not nested objects or arrays |
| String value length | 500 characters |
400 and the payout is not created.
multipart/form-data requests (USD international wire), send metadata as a
single JSON-stringified form field, the same way inlineBeneficiary is sent.Example Response
{
"status": 200,
"message": "Withdrawal initiated successfully",
"success": true,
"data": {
"transaction": {
"id": "866b7abd-6cac-40f2-a04f-d6e58bf47d04",
"transaction_type": "withdrawal",
"status": "pending",
"description": "Vendor payment",
"external_reference": "NG-NFNUJTUW",
"fee_amount": 25,
"source_amount": 5025,
"destination_amount": 5000,
"source_currency": "NGN",
"destination_currency": "NGN",
"created_at": "2026-03-23T13:45:28.138Z",
"updated_at": "2026-03-23T13:45:28.263Z",
"beneficiary": {
"bank_name": "Access Bank",
"bank_code": "000014",
"account_name": "John Doe",
"account_number": "0123456789"
}
}
}
}
source_amount is what left your wallet and destination_amount is what the beneficiary
receives. With the default deductFeesFromBalance: true, source_amount is amount + fee_amount
and destination_amount equals the amount you sent.
The beneficiary block is present only when the payout carried a beneficiary snapshot. Two other
fields are conditional: metadata appears when you sent any, and uetr / tracking_codes appear
on cross-border payouts once the rail returns them, which is never on the response to this call.
For those, read the payout back from
GET /wallet/transaction/{transactionId} or wait
for the webhook rather than expecting them here.
beneficiaryId to pay a saved beneficiary. Use inlineBeneficiary for one-time transfers. Set saveBeneficiary: true to save the inline beneficiary automatically for reuse.amount you specified. Set deductFeesFromBalance: false to take the fee out of the withdrawal amount instead, so the beneficiary receives less than amount.Authorizations
Your Rolla API key
Body
Either beneficiaryId or inlineBeneficiary must be provided.
Amount in smallest units (kobo for NGN; US cents for USD — 100 = 1 USD). Must be a whole number and at least 1.
x >= 15000
Currency code of the wallet the payout leaves
3 - 4"NGN"
Narration for the transaction. Required, 1-500 characters after trimming.
1 - 500"Vendor payment"
Your own unique reference for this transaction. Used on USD and other non-NGN bank rails. NGN and Mobile Money payouts always get a Rolla-generated reference instead (e.g. NG-NFNUJTUW), because the provider imposes its own format.
255ID of a saved beneficiary. Use this OR inlineBeneficiary.
One-time beneficiary details (use instead of beneficiaryId)
Show child attributes
Show child attributes
Save the inline beneficiary for future use
Defaults to true on this API: the fee is taken from your wallet balance on top of amount and the beneficiary receives the exact amount. Set false to take the fee out of the amount instead, so the beneficiary receives less.
Your own key/value data, stored verbatim on the transaction and returned on every response and webhook for it. At most 20 keys; keys up to 64 characters; values must be a string (up to 500 characters), number, boolean or null — not nested objects or arrays.
Show child attributes
Show child attributes
{ "invoice_id": "INV-2026-042", "cost_centre": "AP-LAGOS", "retry": false }
Which of your registered affiliated payer names the beneficiary should see on this payout. Only an international wire can carry one; naming one on any other rail is a 400. Because international wire must be sent as multipart/form-data, a payout carrying payerNameId is always a multipart request. Omit to use your default payer name if you have set one, and your own business name if you have not. Requires Named Payouts to be enabled.