Create Master Wallet Payout
curl --request POST \
--url https://api-production.fossapay.com/api/v1/payouts \
--header 'Content-Type: application/json' \
--header 'X-Idempotency-Key: <x-idempotency-key>' \
--header 'x-api-key: <x-api-key>' \
--data '
{
"amount": 123,
"currency": "<string>",
"reference": "<string>",
"destinationBankCode": "<string>",
"destinationBankName": "<string>",
"destinationAccountName": "<string>",
"destinationAccountNumber": "<string>",
"remarks": "<string>",
"metadata": {}
}
'import requests
url = "https://api-production.fossapay.com/api/v1/payouts"
payload = {
"amount": 123,
"currency": "<string>",
"reference": "<string>",
"destinationBankCode": "<string>",
"destinationBankName": "<string>",
"destinationAccountName": "<string>",
"destinationAccountNumber": "<string>",
"remarks": "<string>",
"metadata": {}
}
headers = {
"x-api-key": "<x-api-key>",
"X-Idempotency-Key": "<x-idempotency-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-api-key': '<x-api-key>',
'X-Idempotency-Key': '<x-idempotency-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
amount: 123,
currency: '<string>',
reference: '<string>',
destinationBankCode: '<string>',
destinationBankName: '<string>',
destinationAccountName: '<string>',
destinationAccountNumber: '<string>',
remarks: '<string>',
metadata: {}
})
};
fetch('https://api-production.fossapay.com/api/v1/payouts', 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-production.fossapay.com/api/v1/payouts",
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' => 123,
'currency' => '<string>',
'reference' => '<string>',
'destinationBankCode' => '<string>',
'destinationBankName' => '<string>',
'destinationAccountName' => '<string>',
'destinationAccountNumber' => '<string>',
'remarks' => '<string>',
'metadata' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Idempotency-Key: <x-idempotency-key>",
"x-api-key: <x-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-production.fossapay.com/api/v1/payouts"
payload := strings.NewReader("{\n \"amount\": 123,\n \"currency\": \"<string>\",\n \"reference\": \"<string>\",\n \"destinationBankCode\": \"<string>\",\n \"destinationBankName\": \"<string>\",\n \"destinationAccountName\": \"<string>\",\n \"destinationAccountNumber\": \"<string>\",\n \"remarks\": \"<string>\",\n \"metadata\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<x-api-key>")
req.Header.Add("X-Idempotency-Key", "<x-idempotency-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-production.fossapay.com/api/v1/payouts")
.header("x-api-key", "<x-api-key>")
.header("X-Idempotency-Key", "<x-idempotency-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 123,\n \"currency\": \"<string>\",\n \"reference\": \"<string>\",\n \"destinationBankCode\": \"<string>\",\n \"destinationBankName\": \"<string>\",\n \"destinationAccountName\": \"<string>\",\n \"destinationAccountNumber\": \"<string>\",\n \"remarks\": \"<string>\",\n \"metadata\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-production.fossapay.com/api/v1/payouts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<x-api-key>'
request["X-Idempotency-Key"] = '<x-idempotency-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 123,\n \"currency\": \"<string>\",\n \"reference\": \"<string>\",\n \"destinationBankCode\": \"<string>\",\n \"destinationBankName\": \"<string>\",\n \"destinationAccountName\": \"<string>\",\n \"destinationAccountNumber\": \"<string>\",\n \"remarks\": \"<string>\",\n \"metadata\": {}\n}"
response = http.request(request)
puts response.read_bodyMaster Wallet Payouts
Create Master Wallet Payout
Send NGN directly from your business master wallet
POST
/
api
/
v1
/
payouts
Create Master Wallet Payout
curl --request POST \
--url https://api-production.fossapay.com/api/v1/payouts \
--header 'Content-Type: application/json' \
--header 'X-Idempotency-Key: <x-idempotency-key>' \
--header 'x-api-key: <x-api-key>' \
--data '
{
"amount": 123,
"currency": "<string>",
"reference": "<string>",
"destinationBankCode": "<string>",
"destinationBankName": "<string>",
"destinationAccountName": "<string>",
"destinationAccountNumber": "<string>",
"remarks": "<string>",
"metadata": {}
}
'import requests
url = "https://api-production.fossapay.com/api/v1/payouts"
payload = {
"amount": 123,
"currency": "<string>",
"reference": "<string>",
"destinationBankCode": "<string>",
"destinationBankName": "<string>",
"destinationAccountName": "<string>",
"destinationAccountNumber": "<string>",
"remarks": "<string>",
"metadata": {}
}
headers = {
"x-api-key": "<x-api-key>",
"X-Idempotency-Key": "<x-idempotency-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-api-key': '<x-api-key>',
'X-Idempotency-Key': '<x-idempotency-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
amount: 123,
currency: '<string>',
reference: '<string>',
destinationBankCode: '<string>',
destinationBankName: '<string>',
destinationAccountName: '<string>',
destinationAccountNumber: '<string>',
remarks: '<string>',
metadata: {}
})
};
fetch('https://api-production.fossapay.com/api/v1/payouts', 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-production.fossapay.com/api/v1/payouts",
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' => 123,
'currency' => '<string>',
'reference' => '<string>',
'destinationBankCode' => '<string>',
'destinationBankName' => '<string>',
'destinationAccountName' => '<string>',
'destinationAccountNumber' => '<string>',
'remarks' => '<string>',
'metadata' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Idempotency-Key: <x-idempotency-key>",
"x-api-key: <x-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-production.fossapay.com/api/v1/payouts"
payload := strings.NewReader("{\n \"amount\": 123,\n \"currency\": \"<string>\",\n \"reference\": \"<string>\",\n \"destinationBankCode\": \"<string>\",\n \"destinationBankName\": \"<string>\",\n \"destinationAccountName\": \"<string>\",\n \"destinationAccountNumber\": \"<string>\",\n \"remarks\": \"<string>\",\n \"metadata\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<x-api-key>")
req.Header.Add("X-Idempotency-Key", "<x-idempotency-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-production.fossapay.com/api/v1/payouts")
.header("x-api-key", "<x-api-key>")
.header("X-Idempotency-Key", "<x-idempotency-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 123,\n \"currency\": \"<string>\",\n \"reference\": \"<string>\",\n \"destinationBankCode\": \"<string>\",\n \"destinationBankName\": \"<string>\",\n \"destinationAccountName\": \"<string>\",\n \"destinationAccountNumber\": \"<string>\",\n \"remarks\": \"<string>\",\n \"metadata\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-production.fossapay.com/api/v1/payouts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<x-api-key>'
request["X-Idempotency-Key"] = '<x-idempotency-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 123,\n \"currency\": \"<string>\",\n \"reference\": \"<string>\",\n \"destinationBankCode\": \"<string>\",\n \"destinationBankName\": \"<string>\",\n \"destinationAccountName\": \"<string>\",\n \"destinationAccountNumber\": \"<string>\",\n \"remarks\": \"<string>\",\n \"metadata\": {}\n}"
response = http.request(request)
puts response.read_bodyCreates a payout from the authenticated merchant’s permanent NGN master wallet. You do not send a
customerId, wallet ID, or source account—the API derives the source wallet from your live API key.
The destination account number determines how the payout is routed:
| Destination | Route returned | Fee |
|---|---|---|
| A customer wallet belonging to your business | same_business | Free |
| A FossaPay wallet belonging to another business | fossapay_cross_business | Applicable payout fee |
| An external Nigerian bank account | external_bank | Applicable payout fee |
Perform a Bank Name Enquiry first and submit the returned bank code, bank name, account name, and account number.
Headers
string
required
An active production FossaPay API key with full permission. The associated business must be verified.
string
required
A unique key of up to 255 characters for this logical payout.
Idempotency-Key is also accepted. If both headers are sent, their values must match.Body
integer
required
Amount the beneficiary should receive in NGN. Minimum
1.string
required
Currency code. Currently only
NGN is supported.string
required
Your unique payout reference, 1–100 characters. Allowed characters are letters, numbers,
., _, :, and -.string
required
Bank code returned by the supported-banks or bank-name-enquiry endpoint.
string
required
Beneficiary bank name, up to 199 characters.
string
required
Verified beneficiary account name, up to 199 characters.
string
required
Beneficiary account number, up to 199 characters. FossaPay uses this value to detect same-business and cross-business wallet destinations automatically.
string
Optional payout narration, up to 255 characters.
object
Optional merchant-defined JSON, up to 4,096 UTF-8 bytes. It is returned unchanged in API responses and payout webhooks.
Request
curl --request POST \
--url https://api-production.fossapay.com/api/v1/payouts \
--header 'x-api-key: YOUR_LIVE_API_KEY' \
--header 'X-Idempotency-Key: payout-order-10025-v1' \
--header 'Content-Type: application/json' \
--data '{
"amount": 5000,
"currency": "NGN",
"reference": "PAYOUT-10025",
"destinationBankCode": "058",
"destinationBankName": "Guaranty Trust Bank",
"destinationAccountName": "ADA LOVELACE",
"destinationAccountNumber": "0123456789",
"remarks": "Vendor settlement",
"metadata": {
"vendorId": "VEN-42",
"invoiceId": "INV-10025"
}
}'
Response
The endpoint returns HTTP202. A same-business transfer may already be completed; other routes normally begin as processing and reach a terminal state asynchronously.
{
"status": "success",
"statusCode": 202,
"message": "Payout request processed successfully",
"data": {
"payoutId": "a9af907d-965f-4ad8-9472-f437fc11df21",
"transactionId": "5a0bb85c-268b-44fa-a57d-70b2944688bc",
"reference": "PAYOUT-10025",
"status": "processing",
"route": "external_bank",
"amount": "5000.00",
"fee": "80.00",
"totalDebited": "5080.00",
"currency": "NGN",
"recipient": {
"bankCode": "058",
"bankName": "Guaranty Trust Bank",
"accountName": "ADA LOVELACE",
"accountNumber": "0123456789"
},
"providerReference": "PYT_K8Q4D2M7N5R3T9A_1789098000000",
"metadata": {
"vendorId": "VEN-42",
"invoiceId": "INV-10025"
},
"completedAt": null,
"failedAt": null,
"reversedAt": null
}
}
Idempotency
- Retrying the identical request with the same idempotency key returns the original payout without another debit.
- Reusing an idempotency key with a different body returns HTTP
409. - Each
referencemust also be unique for the authenticated merchant. - If a request times out, retrieve the payout by reference before deciding whether to retry. Never retry a payout with a new key merely because the first response was delayed.
Statuses
| Status | Meaning |
|---|---|
processing | Accepted and awaiting a final provider result. Do not retry. |
completed | The payout completed successfully. |
reversed | The payout did not complete and the reserved funds were returned. |
failed | The payout failed. Retrieve its latest state and contact support if funds appear reserved. |
Errors
| HTTP status | When it occurs |
|---|---|
400 | Invalid body, missing/conflicting idempotency headers, missing master wallet, self-payment, insufficient funds, or unavailable payout configuration/provider. |
401 | Missing, invalid, inactive, revoked, non-live, or non-full-permission API key. |
403 | The merchant has not completed business verification. |
409 | Duplicate reference or incompatible reuse of an idempotency key. |
Treat
processing as non-terminal. Confirm the outcome using a signed payout webhook or either retrieval endpoint before marking a payout final.
