Payouts
Send KES to M-Pesa, Airtel Money, Tills, PayBills and banks: destinations, amounts, statuses, failures, quotes and fees.
A payout sends Kenyan shillings to one recipient, paid from your USDC balance. This page covers everything about them; the endpoint details are in the Payouts reference.
Destinations
Choose the destination by type. Your workspace may be limited to some types (limits.methods in GET /api/v1/account); others get METHOD_UNAVAILABLE.
// M-Pesa or Airtel Money wallet
{ "type": "mobile_money", "phone": "+254712345678", "network": "safaricom", "name": "Jane Wanjiku" }
// Till (Buy Goods)
{ "type": "till", "till_number": "5123456" }
// PayBill
{ "type": "paybill", "paybill_number": "247247", "account_number": "INV-2041" }
// Kenyan bank account (bank_code from GET /api/v1/banks)
{ "type": "bank", "bank_code": "KCBLKENX", "account_number": "1234567890", "account_name": "Jane Wanjiku" }| Type | Fields | Rules |
|---|---|---|
mobile_money | phone, network?, name? | A Kenyan mobile number in any common format (+254712345678, 254712345678, 0712345678). network: safaricom (default) or airtel. name: who you expect, for checks. |
till | till_number | 5 to 8 digits. |
paybill | paybill_number, account_number | PayBill 5 to 7 digits; account number 1 to 20 characters, exactly as the business gave it. |
bank | bank_code, account_number, account_name | bank_code from GET /api/v1/banks; account number 6 to 34 characters; the holder's name. |
Amounts
amountis what the recipient receives, in KES cents, as an integer:150000is KES 1,500.- Whole shillings only: the amount must be a multiple of 100, or you get
VALIDATION_ERROR. - Fees are added on top. The total taken from your balance is
debited_usdcon the payout. - USDC amounts are decimal strings with 6 places (
"11.948310"). Don't parse them into floats for accounting; use a decimal type.
Sending one
curl -X POST "https://api.autopayke.com/api/v1/payouts" \
-H "Authorization: Bearer $AUTOPAYKE_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"amount": 150000,
"currency": "KES",
"destination": { "type": "mobile_money", "phone": "+254700000001" },
"reference": "order_1042"
}'What happens when you call it:
- We check the Idempotency-Key, your key's scope and allowlist, and whether payouts are paused.
- We validate the destination and check your per-payout, daily and monthly limits.
- We price it at the current rate and check your balance covers the amount plus fees.
- We take the total from your balance, record the payout and answer with it, status
pending. - We send the money. Webhooks follow as it moves.
If any check fails, nothing is charged, you get an error code, and you can retry with the same Idempotency-Key.
Statuses
pending ──► processing ──► succeeded ──► reversed
│ │
│ ├──► in_review ──► processing / succeeded / failed
│ │
└─────────────┴──► failed| Status | Final? | Meaning | Webhook |
|---|---|---|---|
pending | No | Accepted and paid for from your balance; about to be sent. | payout.created |
processing | No | On its way to the recipient's network. | payout.processing |
in_review | No | Held for a manual check before it's paid. Usually decided within hours. | payout.in_review |
succeeded | Usually | The recipient has the money. | payout.succeeded |
failed | Yes | It didn't go through. Nothing was paid; any debit is back in your balance. | payout.failed |
reversed | Yes | It succeeded, then the money came back to your balance (rare: e.g. the network returned it). | payout.reversed |
Treat succeeded as done for your customer, but keep listening: a reversal can arrive later. Mobile money usually completes in under a minute; banks can take longer, depending on the bank.
Why payouts fail
| failure_code | Meaning | What to do |
|---|---|---|
RECIPIENT_INVALID | The number or account can't receive this payout (closed, wrong, or not registered for mobile money). | Ask the recipient for different details. |
METHOD_UNAVAILABLE | The payout network didn't answer in time. Nothing was paid. | Retry later with a new Idempotency-Key. |
FUNDING_FAILED | We couldn't fund it from your wallet. Nothing left the wallet. | Check your balance and retry. |
PAYOUT_FAILED | It couldn't be completed. Any money taken is returned. | Retry later; contact support with the payout id if it repeats. |
PAYOUT_RETURNED | The money was sent back to your wallet before completing. | Check the recipient's details. |
A failed payout is final. To try again, create a new payout with a new Idempotency-Key. Reusing the old key returns the old, failed payout.
Quotes
POST /api/v1/payouts/quote takes the same body as a payout (minus reference, description and metadata) and returns the rate, fee_usdc, total_usdc and a fee_breakdown, after running the same destination and limit checks. It doesn't hold money or the rate; creating the payout prices it again, and the rate can move slightly in between. Quotes are free and don't count against limits.
Fees
You pay our cash-out fee less 0.5 percentage points (the API partner price) plus the Avalanche network fee. Depending on how fees are configured, the payout network's own charge is either inside our fee or shown separately as payout_cost_usdc. The quote and the payout always show exactly what you paid. See Fees and pricing for today's published rates.
References and metadata
Put your own id in reference (up to 64 characters) and anything else you'll want back in metadata (up to 20 string pairs). Both come back on the payout, its events and webhooks, and you can list payouts by reference. Don't store personal data you don't need, secrets or card numbers there.
Paying many people
There's no batch endpoint yet; send one payout per person from a queue. Give each its own Idempotency-Key derived from your batch and line (payroll-2026-11:line-42), keep under your requests-a-second limit, check your balance first with GET /api/v1/balance, and reconcile from webhooks.