Create an immediate bulk payout batch
curl --request POST \
--url https://api-production.fossapay.com/api/v1/payouts/batches \
--header 'Content-Type: multipart/form-data' \
--header 'X-Idempotency-Key: <x-idempotency-key>' \
--header 'x-api-key: <api-key>' \
--form 'accountNumber=<string>' \
--form 'batchReference=<string>' \
--form executionMode=IMMEDIATE \
--form csvFile='@example-file'import requests
url = "https://api-production.fossapay.com/api/v1/payouts/batches"
files = { "csvFile": ("example-file", open("example-file", "rb")) }
payload = {
"accountNumber": "<string>",
"batchReference": "<string>",
"executionMode": "IMMEDIATE"
}
headers = {
"X-Idempotency-Key": "<x-idempotency-key>",
"x-api-key": "<api-key>"
}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('accountNumber', '<string>');
form.append('batchReference', '<string>');
form.append('executionMode', 'IMMEDIATE');
form.append('csvFile', '<string>');
const options = {
method: 'POST',
headers: {'X-Idempotency-Key': '<x-idempotency-key>', 'x-api-key': '<api-key>'}
};
options.body = form;
fetch('https://api-production.fossapay.com/api/v1/payouts/batches', 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/batches",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"accountNumber\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"batchReference\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"executionMode\"\r\n\r\nIMMEDIATE\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"csvFile\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data",
"X-Idempotency-Key: <x-idempotency-key>",
"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-production.fossapay.com/api/v1/payouts/batches"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"accountNumber\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"batchReference\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"executionMode\"\r\n\r\nIMMEDIATE\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"csvFile\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Idempotency-Key", "<x-idempotency-key>")
req.Header.Add("x-api-key", "<api-key>")
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/batches")
.header("X-Idempotency-Key", "<x-idempotency-key>")
.header("x-api-key", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"accountNumber\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"batchReference\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"executionMode\"\r\n\r\nIMMEDIATE\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"csvFile\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-production.fossapay.com/api/v1/payouts/batches")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Idempotency-Key"] = '<x-idempotency-key>'
request["x-api-key"] = '<api-key>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"accountNumber\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"batchReference\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"executionMode\"\r\n\r\nIMMEDIATE\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"csvFile\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"status": "success",
"statusCode": 200,
"message": "<string>",
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reference": "<string>",
"accountNumber": "<string>",
"status": "processing",
"executionMode": "IMMEDIATE",
"totalRecipients": 123,
"totalAmount": "<string>",
"totalFee": "<string>",
"totalDebit": "<string>",
"failureReason": "<string>",
"completedAt": "2023-11-07T05:31:56Z",
"failedAt": "2023-11-07T05:31:56Z",
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
}
}{
"status": "<string>",
"statusCode": 123,
"message": "<string>",
"error": "<string>",
"code": "<string>",
"errors": {}
}{
"status": "<string>",
"statusCode": 123,
"message": "<string>",
"error": "<string>",
"code": "<string>",
"errors": {}
}{
"status": "<string>",
"statusCode": 123,
"message": "<string>",
"error": "<string>",
"code": "<string>",
"errors": {}
}{
"status": "<string>",
"statusCode": 123,
"message": "<string>",
"error": "<string>",
"code": "<string>",
"errors": {}
}{
"status": "<string>",
"statusCode": 123,
"message": "<string>",
"error": "<string>",
"code": "<string>",
"errors": {}
}Bulk Payouts
Create Bulk Payout Batch
Validate, fund, and submit an immediate NGN payout batch from a CSV file
POST
/
api
/
v1
/
payouts
/
batches
Create an immediate bulk payout batch
curl --request POST \
--url https://api-production.fossapay.com/api/v1/payouts/batches \
--header 'Content-Type: multipart/form-data' \
--header 'X-Idempotency-Key: <x-idempotency-key>' \
--header 'x-api-key: <api-key>' \
--form 'accountNumber=<string>' \
--form 'batchReference=<string>' \
--form executionMode=IMMEDIATE \
--form csvFile='@example-file'import requests
url = "https://api-production.fossapay.com/api/v1/payouts/batches"
files = { "csvFile": ("example-file", open("example-file", "rb")) }
payload = {
"accountNumber": "<string>",
"batchReference": "<string>",
"executionMode": "IMMEDIATE"
}
headers = {
"X-Idempotency-Key": "<x-idempotency-key>",
"x-api-key": "<api-key>"
}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('accountNumber', '<string>');
form.append('batchReference', '<string>');
form.append('executionMode', 'IMMEDIATE');
form.append('csvFile', '<string>');
const options = {
method: 'POST',
headers: {'X-Idempotency-Key': '<x-idempotency-key>', 'x-api-key': '<api-key>'}
};
options.body = form;
fetch('https://api-production.fossapay.com/api/v1/payouts/batches', 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/batches",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"accountNumber\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"batchReference\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"executionMode\"\r\n\r\nIMMEDIATE\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"csvFile\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data",
"X-Idempotency-Key: <x-idempotency-key>",
"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-production.fossapay.com/api/v1/payouts/batches"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"accountNumber\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"batchReference\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"executionMode\"\r\n\r\nIMMEDIATE\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"csvFile\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Idempotency-Key", "<x-idempotency-key>")
req.Header.Add("x-api-key", "<api-key>")
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/batches")
.header("X-Idempotency-Key", "<x-idempotency-key>")
.header("x-api-key", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"accountNumber\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"batchReference\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"executionMode\"\r\n\r\nIMMEDIATE\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"csvFile\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-production.fossapay.com/api/v1/payouts/batches")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Idempotency-Key"] = '<x-idempotency-key>'
request["x-api-key"] = '<api-key>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"accountNumber\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"batchReference\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"executionMode\"\r\n\r\nIMMEDIATE\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"csvFile\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"status": "success",
"statusCode": 200,
"message": "<string>",
"data": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"reference": "<string>",
"accountNumber": "<string>",
"status": "processing",
"executionMode": "IMMEDIATE",
"totalRecipients": 123,
"totalAmount": "<string>",
"totalFee": "<string>",
"totalDebit": "<string>",
"failureReason": "<string>",
"completedAt": "2023-11-07T05:31:56Z",
"failedAt": "2023-11-07T05:31:56Z",
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
}
}{
"status": "<string>",
"statusCode": 123,
"message": "<string>",
"error": "<string>",
"code": "<string>",
"errors": {}
}{
"status": "<string>",
"statusCode": 123,
"message": "<string>",
"error": "<string>",
"code": "<string>",
"errors": {}
}{
"status": "<string>",
"statusCode": 123,
"message": "<string>",
"error": "<string>",
"code": "<string>",
"errors": {}
}{
"status": "<string>",
"statusCode": 123,
"message": "<string>",
"error": "<string>",
"code": "<string>",
"errors": {}
}{
"status": "<string>",
"statusCode": 123,
"message": "<string>",
"error": "<string>",
"code": "<string>",
"errors": {}
}Creates an immediate, all-or-nothing payout batch funded by the authenticated merchant’s master wallet or one of its subaccounts. The configured single-payout fee is charged for every CSV row.
The required header must be the first non-empty row and fields must be separated with commas. Remove spreadsheet column-label rows such as
Do not set
CSV columns
| Column | Rules |
|---|---|
Account Number | Exactly 10 digits |
Bank Name | A name from the supported-bank directory |
Amount | Positive NGN amount with at most two decimal places |
Narration | Required; maximum 255 characters |
A,B,C,D; semicolon- and tab-delimited exports are not accepted.
Request
curl --request POST \
--url https://api-production.fossapay.com/api/v1/payouts/batches \
--header "x-api-key: $FOSSAPAY_API_KEY" \
--header 'X-Idempotency-Key: payroll-2026-09-v1' \
--form 'accountNumber=9710000000' \
--form 'batchReference=PAYROLL-2026-09' \
--form 'executionMode=IMMEDIATE' \
--form 'csvFile=@./payroll-2026-09.csv;type=text/csv'
Content-Type manually; your client must include the generated multipart boundary. A successful 202 response means the batch was accepted for processing, not that recipients have been paid.
Idempotency
The same key, form fields, and exact CSV bytes return the original batch. Changed fields or file bytes with the same key return409. The batchReference must be globally unique.
See the Bulk Payouts guide for the CSV format, processing lifecycle, retries, webhooks, and scheduling guidance.Authorizations
Headers
Unique key for this logical batch. Idempotency-Key is also accepted; if both headers are sent, they must match.
Maximum string length:
255Body
multipart/form-data
Merchant-owned master wallet or subaccount number funding the batch.
Globally unique merchant-defined batch reference.
Maximum string length:
255Available options:
IMMEDIATE UTF-8 CSV containing Account Number, Bank Name, Amount, and Narration columns.

