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"
}
'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'
})
};
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"
}
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}")
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",
"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.
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"
}
'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'
})
};
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"
}
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}")
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",
"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
Recommended. If the same key is retried with an identical request body, the cached response is returned rather than sending a second payout. Retrying the same key with a different body returns 409 IDEMPOTENCY_CONFLICT.
Pay out on behalf of an account you own rather than your own account. The destination, balance, and payout are all scoped to that party.
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.
The ID of a payout destination belonging to your account. The destination must be usable (is_usable: true).
"pd_7Kq2mNv4XbR9dLc0"
The amount the destination should receive, as a decimal string (e.g. "5000.00"), in the destination's currency. The fee is charged on top of this amount, not deducted from it. Omit when supplying quote_id.
"5000.00"
A quote ID from Create Payout Quote. Required for cross-currency payouts, where the source currency differs from the destination's currency. Omit amount when supplying this field.
null
Your own reference for this payout, up to 128 characters. Omit it and the payout has none; use the payout's id to track it.
128"payout-2026-08-07-001"
Arbitrary key-value data to attach to the payout.
null
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 payout ID. Use this to look it up with Get Payout.
"pay_4Xr9dLc0mNv7Kq2B"
pending: accepted and queued. processing: submitted to the payment rail. completed: delivered to the destination. failed: could not be delivered, see failure_reason.
pending, processing, completed, failed "pending"
The net amount delivered to the destination, in currency.
"5000.00"
The destination's currency. amount is denominated in this currency.
"NGN"
The currency of the balance being debited. fee and total_debited are denominated in this currency. Equal to currency for a same-currency payout; different for a cross-currency payout funded with a quote_id.
"NGN"
The fee charged for this payout, in source_currency.
"100.00"
The gross amount debited from your balance, in source_currency. Equals amount + fee only for a same-currency payout, because on a cross-currency payout amount is in a different currency, so the two do not add up.
"5100.00"
The payout destination ID this payout was sent to.
"pd_7Kq2mNv4XbR9dLc0"
The reference you set when you created the payout. null if you set none.
"payout-2026-08-07-001"
Populated only when status is failed.
null
ISO 8601 creation timestamp.
"2026-08-07T14:30:00.000Z"
ISO 8601 timestamp of when the payout reached a terminal state. null while status is pending or processing.
null

