Send
Payouts
POST /v1/payouts sends money from your available balance. The header Idempotency-Key is required. AFRIPEI reserves the amount plus the fee before the payout is sent. If the balance cannot cover both, the response is 409 INSUFFICIENT_BALANCE and no payout row is created.
A payout moves real TZS. Read the balance first with GET /v1/balance. The available figure is what you can send.
How a payout works
- 1Read the balance.
GET /v1/balancereturns available, pending, and reserved amounts. Available is what a new payout can reserve. - 2Create the payout.
POST /v1/payoutsreserves the amount plus the fee, then hands it to the rail.409 INSUFFICIENT_BALANCEcreates no row. - 3Wait for the result.
payout.successmeans the recipient was paid.payout.failedreleases that reservation.
Fields
POST /v1/payouts
| Field | Required | What it is |
|---|---|---|
amount | Yes | Decimal string with two places, such as 10000.00. |
currency | Yes | TZS. |
method | Yes | mobile_money or bank. |
recipient.phone | Mobile money | E.164. |
recipient.name | Yes for bank | Wallet name, or the bank account name. |
recipient.account_number | Bank | The account to credit. |
recipient.bic | Bank | Bank identifier from GET /v1/banks. |
recipient.transfer_type | Bank | ACH or RTGS. |
merchant_reference | No | Your own id for the payout. |
Idempotency-Key | Yes | Header. Your key, kept for 72 hours. |
Mobile money
Set method to mobile_money and recipient.phone to a Tanzania number in E.164, beginning with +255. The recipient confirms nothing on a payout. The money is sent to M-Pesa, Airtel Money, Mixx by Yas, or Halopesa on that number.
{
"amount": "10000.00",
"currency": "TZS",
"method": "mobile_money",
"merchant_reference": "salary-1001",
"recipient": { "phone": "+255700000000", "name": "Amina Juma" }
}Bank transfer
Set method to bank. recipient.account_number, recipient.bic, and recipient.transfer_type are required. transfer_type is ACH or RTGS. GET /v1/banks lists the banks you can pay.
{
"amount": "250000.00",
"currency": "TZS",
"method": "bank",
"merchant_reference": "supplier-44",
"recipient": {
"name": "Amina Supplies",
"account_number": "0152000000000",
"bic": "NMIBTZTZ",
"bank_name": "NMB",
"transfer_type": "ACH"
}
}Bulk
POST /v1/payouts/bulk reserves the whole batch first. Each item has its own amount and phone. A failed item releases only that item. The batch still uses one Idempotency-Key.
{
"currency": "TZS",
"items": [
{ "amount": "5000.00", "phone": "+255700000000", "name": "Amina Juma" },
{ "amount": "5000.00", "phone": "+255700000001", "name": "Baraka Mushi" }
]
}Response
POST /v1/payouts
A 201 means the payout was reserved and handed to the rail. fees.merchant_total is the amount plus the fee that left available balance. fees.recipient_amount is what the recipient is due. The amounts in the example are the shape, not a price list.
{
"id": "po_...",
"object": "payout",
"amount": "10000.00",
"currency": "TZS",
"status": "pending",
"method": "mobile_money",
"provider": "mobile_money",
"provider_name": "Mobile money",
"merchant_reference": "salary-1001",
"recipient": { "phone": "+255700000000", "name": "Amina Juma" },
"fees": {
"afripei_fee": "0.00",
"merchant_total": "10000.00",
"recipient_amount": "10000.00"
},
"livemode": true,
"created_at": "2026-10-20T12:00:00.000Z",
"completed_at": null
}Statuses
| Status | Meaning |
|---|---|
| created | Reserved. Not sent yet. |
| initiated | Handed to the rail. |
| pending | The rail has accepted it and has not finished. |
| processing | The transfer is being applied. |
| success | The recipient was paid. |
| failed | The rail rejected it. The reservation is released. |
| cancelled | Stopped before the money left. |
Read one payout with GET /v1/payouts/:id and list them with GET /v1/payouts.
Balance before you send
GET /v1/balance returns available, pending, and reserved amounts. Available is what a new payout can reserve. Pending is money that has not settled into available. Reserved is held for payouts that have not finished. A payout that is still pending stays inside reserved until it succeeds or fails.
Banks
GET /v1/banks lists the banks a payout can use. Use the identifier from that list as recipient.bic. Do not invent a bank code. transfer_type is ACH or RTGS. The account name is the name on the bank account.
What not to do
- Do not create a payout when you have not read the balance.
- Do not retry a timed-out create with a new idempotency key. The first request may already have reserved the money.
- Do not treat
pendingas paid. Wait forpayout.success. - Do not send a fee in the body. The fee is reserved from the rule on the account.