curl --request POST \
--url https://api.bachs.io/v1/transfers \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"destination": "acct_3Wq8ZfT1yHnJ5sVe",
"amount": "7000.00",
"currency": "NGN",
"transfer_group": "ch_9f4c1d2e7b6a4f8e9c0d1a2b3c4d5e6f",
"description": "Order #4471 seller share"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
destination: 'acct_3Wq8ZfT1yHnJ5sVe',
amount: '7000.00',
currency: 'NGN',
transfer_group: 'ch_9f4c1d2e7b6a4f8e9c0d1a2b3c4d5e6f',
description: 'Order #4471 seller share'
})
};
fetch('https://api.bachs.io/v1/transfers', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.bachs.io/v1/transfers"
payload = {
"destination": "acct_3Wq8ZfT1yHnJ5sVe",
"amount": "7000.00",
"currency": "NGN",
"transfer_group": "ch_9f4c1d2e7b6a4f8e9c0d1a2b3c4d5e6f",
"description": "Order #4471 seller share"
}
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/transfers"
payload := strings.NewReader("{\n \"destination\": \"acct_3Wq8ZfT1yHnJ5sVe\",\n \"amount\": \"7000.00\",\n \"currency\": \"NGN\",\n \"transfer_group\": \"ch_9f4c1d2e7b6a4f8e9c0d1a2b3c4d5e6f\",\n \"description\": \"Order #4471 seller share\"\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": "tr_8c1e04a7b93f2d6540ab",
"source": "acct_7KpQ2mNv4XbR9dLc",
"destination": "acct_3Wq8ZfT1yHnJ5sVe",
"amount": "7000.00",
"currency": "NGN",
"status": "paid",
"description": "Order #4471 seller share",
"metadata": {},
"transfer_group": "ch_9f4c1d2e7b6a4f8e9c0d1a2b3c4d5e6f",
"kind": "manual",
"source_charge_id": null,
"created_at": "2026-08-07T11:04:22.518Z"
}{
"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"
}Create a transfer
Move funds between your platform balance and an account you own. This debits the source balance immediately and cannot be cancelled. Transfers draw on available balance only, move a single currency, and never take a balance below zero. See the Split payments guide for the full flow.
curl --request POST \
--url https://api.bachs.io/v1/transfers \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"destination": "acct_3Wq8ZfT1yHnJ5sVe",
"amount": "7000.00",
"currency": "NGN",
"transfer_group": "ch_9f4c1d2e7b6a4f8e9c0d1a2b3c4d5e6f",
"description": "Order #4471 seller share"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
destination: 'acct_3Wq8ZfT1yHnJ5sVe',
amount: '7000.00',
currency: 'NGN',
transfer_group: 'ch_9f4c1d2e7b6a4f8e9c0d1a2b3c4d5e6f',
description: 'Order #4471 seller share'
})
};
fetch('https://api.bachs.io/v1/transfers', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.bachs.io/v1/transfers"
payload = {
"destination": "acct_3Wq8ZfT1yHnJ5sVe",
"amount": "7000.00",
"currency": "NGN",
"transfer_group": "ch_9f4c1d2e7b6a4f8e9c0d1a2b3c4d5e6f",
"description": "Order #4471 seller share"
}
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/transfers"
payload := strings.NewReader("{\n \"destination\": \"acct_3Wq8ZfT1yHnJ5sVe\",\n \"amount\": \"7000.00\",\n \"currency\": \"NGN\",\n \"transfer_group\": \"ch_9f4c1d2e7b6a4f8e9c0d1a2b3c4d5e6f\",\n \"description\": \"Order #4471 seller share\"\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": "tr_8c1e04a7b93f2d6540ab",
"source": "acct_7KpQ2mNv4XbR9dLc",
"destination": "acct_3Wq8ZfT1yHnJ5sVe",
"amount": "7000.00",
"currency": "NGN",
"status": "paid",
"description": "Order #4471 seller share",
"metadata": {},
"transfer_group": "ch_9f4c1d2e7b6a4f8e9c0d1a2b3c4d5e6f",
"kind": "manual",
"source_charge_id": null,
"created_at": "2026-08-07T11:04:22.518Z"
}{
"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"
}Authorizations
Bearer token authentication. Pass your API key as Authorization: Bearer sk_.... See Authentication for keys, scopes, and sandbox vs production.
Headers
Act as this account, making it the debited side. Send it with destination: "self" to recover funds back to your platform.
Body
The account to credit, or self to send funds back to your platform when acting as an account you own with X-Account-Id. The debited side is always whoever is authenticated, so there is no source field.
"acct_3Wq8ZfT1yHnJ5sVe"
Amount to move as a decimal string in currency, e.g. "7000.00". Always two decimal places. Must be greater than zero.
"7000.00"
Three-letter ISO 4217 currency code for the transfer, e.g. NGN. Both balances must already hold this currency; a transfer never converts.
3 - 10"NGN"
An arbitrary string attached to the transfer and returned unchanged. Useful for naming the order or invoice the share belongs to.
500"Order #4471 seller share"
Your own key-value data, returned unchanged on the transfer. Not used by Bachs for processing.
Tags this transfer as part of a group, so several shares funded by the same charge can be reconciled together. Reuse the same value across every transfer for one charge, including a later recovery.
255"ch_9f4c1d2e7b6a4f8e9c0d1a2b3c4d5e6f"
Response
Transfer created and the balances updated.
Unique identifier for this transfer.
"tr_8c1e04a7b93f2d6540ab"
Whoever was debited. Your platform when sending a share to an account you own, the account when recovering one.
"acct_7KpQ2mNv4XbR9dLc"
Whoever was credited.
"acct_3Wq8ZfT1yHnJ5sVe"
The amount moved, as a decimal string in currency.
"7000.00"
Three-letter ISO 4217 currency code the transfer moved.
"NGN"
Whether the funds have moved. paid: the balances have been updated. pending: the transfer exists but its movement has not been recorded yet. Derived from the underlying movement, so it never disagrees with the balances.
pending, paid "paid"
The description you supplied on creation.
"Order #4471 seller share"
The metadata you supplied on creation, returned unchanged.
The group you supplied on creation, or null when the transfer was not tagged.
"ch_9f4c1d2e7b6a4f8e9c0d1a2b3c4d5e6f"
What this movement is. payout is a seller's share of a sale you made, and manual is a transfer you created yourself. The platform's own cut of a sale is never a transfer; read it at Platform fees.
payout, manual "manual"
The charge that funded this movement, when one did. Null on a transfer you created yourself, which is tied to nothing.
null
When the transfer was created, ISO 8601 in UTC.
"2026-08-07T11:04:22.518Z"

