curl --request POST \
--url https://api.bachs.io/v1/payouts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"destination": "pd_7Kq2mNv4XbR9dLc0",
"amount": "5000.00",
"reference": "payout-2026-08-07-001",
"narration": "Invoice 1042"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
destination: 'pd_7Kq2mNv4XbR9dLc0',
amount: '5000.00',
reference: 'payout-2026-08-07-001',
narration: 'Invoice 1042'
})
};
fetch('https://api.bachs.io/v1/payouts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.bachs.io/v1/payouts"
payload = {
"destination": "pd_7Kq2mNv4XbR9dLc0",
"amount": "5000.00",
"reference": "payout-2026-08-07-001",
"narration": "Invoice 1042"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.bachs.io/v1/payouts"
payload := strings.NewReader("{\n \"destination\": \"pd_7Kq2mNv4XbR9dLc0\",\n \"amount\": \"5000.00\",\n \"reference\": \"payout-2026-08-07-001\",\n \"narration\": \"Invoice 1042\"\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))
}{
"id": "pay_4Xr9dLc0mNv7Kq2B",
"status": "pending",
"amount": "5000.00",
"currency": "NGN",
"source_currency": "NGN",
"fee": "100.00",
"total_debited": "5100.00",
"destination": "pd_7Kq2mNv4XbR9dLc0",
"reference": "payout-2026-08-07-001",
"narration": "Invoice 1042",
"session_id": null,
"failure_reason": null,
"created_at": "2026-08-07T14:30:00.000Z"
}{
"detail": "Invalid request parameters",
"error_code": "VALIDATION_ERROR",
"errors": [
{
"field": "amount",
"message": "Amount must be a positive decimal string",
"type": "value_error"
}
]
}{
"detail": "Invalid API key",
"error_code": "UNAUTHORIZED"
}{
"detail": "API key does not have permission for this operation",
"error_code": "FORBIDDEN"
}{
"detail": "Resource not found",
"error_code": "NOT_FOUND"
}{
"detail": "Duplicate request detected",
"error_code": "CONFLICT"
}{
"detail": "Rate limit exceeded. Please retry after a few seconds.",
"error_code": "TOO_MANY_REQUESTS"
}{
"detail": "An unexpected error occurred. Please try again later.",
"error_code": "INTERNAL_SERVER_ERROR"
}Create Payout
Send money to a payout destination you have registered. amount is what the destination receives, and the fee is charged on top, so the balance must cover total_debited. Paying out in a different currency from the balance you are debiting omits amount and passes quote_id instead. A payout to mobile money or to a bank outside Nigeria needs the account’s name, email and address on file, or it is refused with 400 PAYOUT_RECIPIENT_INCOMPLETE, whose detail names what is missing. A connected account also needs international payouts on (400 INTERNATIONAL_PAYOUTS_NOT_ENABLED).
curl --request POST \
--url https://api.bachs.io/v1/payouts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"destination": "pd_7Kq2mNv4XbR9dLc0",
"amount": "5000.00",
"reference": "payout-2026-08-07-001",
"narration": "Invoice 1042"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
destination: 'pd_7Kq2mNv4XbR9dLc0',
amount: '5000.00',
reference: 'payout-2026-08-07-001',
narration: 'Invoice 1042'
})
};
fetch('https://api.bachs.io/v1/payouts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.bachs.io/v1/payouts"
payload = {
"destination": "pd_7Kq2mNv4XbR9dLc0",
"amount": "5000.00",
"reference": "payout-2026-08-07-001",
"narration": "Invoice 1042"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.bachs.io/v1/payouts"
payload := strings.NewReader("{\n \"destination\": \"pd_7Kq2mNv4XbR9dLc0\",\n \"amount\": \"5000.00\",\n \"reference\": \"payout-2026-08-07-001\",\n \"narration\": \"Invoice 1042\"\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))
}{
"id": "pay_4Xr9dLc0mNv7Kq2B",
"status": "pending",
"amount": "5000.00",
"currency": "NGN",
"source_currency": "NGN",
"fee": "100.00",
"total_debited": "5100.00",
"destination": "pd_7Kq2mNv4XbR9dLc0",
"reference": "payout-2026-08-07-001",
"narration": "Invoice 1042",
"session_id": null,
"failure_reason": null,
"created_at": "2026-08-07T14:30:00.000Z"
}{
"detail": "Invalid request parameters",
"error_code": "VALIDATION_ERROR",
"errors": [
{
"field": "amount",
"message": "Amount must be a positive decimal string",
"type": "value_error"
}
]
}{
"detail": "Invalid API key",
"error_code": "UNAUTHORIZED"
}{
"detail": "API key does not have permission for this operation",
"error_code": "FORBIDDEN"
}{
"detail": "Resource not found",
"error_code": "NOT_FOUND"
}{
"detail": "Duplicate request detected",
"error_code": "CONFLICT"
}{
"detail": "Rate limit exceeded. Please retry after a few seconds.",
"error_code": "TOO_MANY_REQUESTS"
}{
"detail": "An unexpected error occurred. Please try again later.",
"error_code": "INTERNAL_SERVER_ERROR"
}Authorizations
Bearer token authentication. Pass your API key as Authorization: Bearer sk_.... See Authentication for keys, scopes, and sandbox vs production.
Headers
Body
Send money to a registered destination. Exactly one of amount or quote_id is required, never both and never neither. amount funds a same-currency payout; quote_id funds a cross-currency payout, since the quote already fixes both sides.
Your own reference for this payout, up to 128 characters: letters, digits, ., - and _, with no spaces. It must be unique on your account. Sending one you have already used returns 400 with Reference already exists for this organization. Use it to find the payout later with List Payouts (GET /v1/payouts?reference=...). Omit it and the payout has none; use the payout's id to track it.
128"payout-2026-08-07-001"
A short note for the recipient's bank statement. Keep it under 15 characters, with the part that matters first, such as an invoice number. We accept up to 100 characters, but many banks show only the first few, and some show their own label instead. Spaces at the start and end are removed, and an empty value counts as not sent. Line breaks and other control characters return 400 with VALIDATION_ERROR. When you send none, bank and mobile money payouts carry Bachs Payout.
100"Invoice 1042"
Response
Success - Payout created
The one shape a payout has on this API, across create, retrieve and list. amount is denominated in currency; fee and total_debited are denominated in source_currency. The two differ on every cross-currency payout, so the debit side must say which one it is in. For a same-currency payout source_currency equals currency.
The narration we sent with this payout: the narration you sent, or our default text when you sent none (Bachs Payout on bank and mobile money payouts). null on a crypto payout without one. The recipient's bank may show it shortened, or show its own label instead.
"Invoice 1042"
The NIP session ID of a payout to a Nigerian bank account: the ID the receiving bank uses to trace the transfer. Give it to the recipient or their bank when they ask where the money is. null until status is completed, and always null on other payout types. Payouts completed before 22 October 2026 have none.
null

