Create a payout
POST /disbursements: send M-Pesa from your merchant payout balance.
Endpoint
POST /api/v1/disbursements
Required scope: disbursements:write. Include X-API-Key, Content-Type: application/json, and Idempotency-Key.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
amount_minor | integer | Yes | KES cents, divisible by 100. Between 100 and 25000000, subject to merchant limits. |
currency | string | Yes | KES |
method | string | No | b2c; also used when omitted |
phone | string | Yes | Recipient's Kenyan M-Pesa mobile number |
reference | string | No | Business reference, up to 80 UTF-8 bytes after trimming |
quoted_fee_minor | integer | No | Expected total withdrawal fee in cents, including the KES 10 service fee |
Approved recipient restrictions and daily limits apply. Unknown and duplicate JSON fields are rejected.
Example request
curl --fail-with-body "$DERIPAY_API_BASE_URL/disbursements" \
-H "X-API-Key: $DERIPAY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: payout-2026-000123" \
--data '{"amount_minor":10000,"currency":"KES","method":"b2c","phone":"254712345678","reference":"PAYOUT-000123"}'Response
A new payout returns 202 Accepted with {"data": payment, "replayed": false}. An idempotent replay returns 200 OK with replayed: true.
The payment object has kind: "disbursement", method: "b2c", its ID, current status, amount, calculated fees, phone, reference, shortcode, and timestamps. See the payment object.
Check payout funds
Read GET /balance before showing payout availability. The request must be covered by both merchant funds and merchant-owned payout liquidity, including applicable fees. A balance response is a snapshot, not a reservation.
MERCHANT_INSUFFICIENT_FUNDS returns HTTP 409. Do not blindly retry it. Recheck the balance and resolve funding first.
Fees
Admission reserves amount_minor + fee_minor + provider_fee_minor. The recipient receives amount_minor; the total withdrawal fee is an additional merchant charge. Every total below includes a KES 10 service fee.
| Withdrawal amount (KES) | Base fee (KES) | Service fee (KES) | Total fee (KES) |
|---|---|---|---|
| 1–100 | 0 | 10 | 10 |
| 101–500 | 7 | 10 | 17 |
| 501–1,000 | 13 | 10 | 23 |
| 1,001–1,500 | 23 | 10 | 33 |
| 1,501–2,500 | 33 | 10 | 43 |
| 2,501–3,500 | 53 | 10 | 63 |
| 3,501–5,000 | 57 | 10 | 67 |
| 5,001–7,500 | 78 | 10 | 88 |
| 7,501–10,000 | 90 | 10 | 100 |
| 10,001–15,000 | 100 | 10 | 110 |
| 15,001–20,000 | 105 | 10 | 115 |
| 20,001–35,000 | 108 | 10 | 118 |
| 35,001–50,000 | 125 | 10 | 135 |
| 50,001–75,000 | 150 | 10 | 160 |
| 75,001–100,000 | 175 | 10 | 185 |
| 100,001–125,000 | 200 | 10 | 210 |
| 125,001–150,000 | 225 | 10 | 235 |
| 150,001–175,000 | 250 | 10 | 260 |
| 175,001–200,000 | 275 | 10 | 285 |
| 200,001–225,000 | 300 | 10 | 310 |
| 225,001–250,000 | 325 | 10 | 335 |
provider_fee_minor records the actual configured M-Pesa B2C business cost. fee_minor records Deripay's retained portion: total withdrawal fee minus that provider cost. The base fee in the table is part of the customer tariff, not an additional provider charge. Do not add any other fee to the table total.
For example, a KES 500 payout (amount_minor: 50000) has a KES 17 total fee: fee_minor: 1200 and provider_fee_minor: 500. It reserves 51700 cents and accepts quoted_fee_minor: 1700. A mismatched quote returns 409 MERCHANT_CONFLICT.
Idempotent retries must retain the original quote and body, even if the fee table has since changed.
Handle pending outcomes
On 202, save the payment ID and wait for confirmation. Poll with disbursements:read or receive a signed terminal webhook.
Do not replace uncertain payouts
If the status is unknown or the create request times out, do not submit a
new payment with a different idempotency key. The original may still complete.
There is no public cancellation endpoint for accepted payouts. Revoking an API key does not cancel them.