Guide · 05 / 10

Payouts

Move money out with single and bulk disbursements.

A payout moves cleared funds out of one of your wallets to a destination. Payouts are their own operation class: the key you use must have the payout scope (payout_only or full) — see Authentication → Scopes.

Amounts are integer minor units. Destinations are always tokenized references (destination_ref) obtained through the payout-destination tokenization flow — you never send raw account numbers.

Single payout

POST /payouts — requires an Idempotency-Key.

Request (PayoutCreateRequest):

FieldRequiredNotes
wallet_idyesSource wallet (UUID).
destination_typeyesmobile_money, bank, or card.
destination_refyesTokenized destination reference.
amountyesMinor units, >= 1.
currencyyesISO-4217.
scheduled_atnoISO-8601; schedule the payout for the future. Omit to send now.
curl -sS https://sandbox.api.wasaapay.com/v1/payouts \
  -H "X-WasaaPay-Key: $KEY" -H "X-WasaaPay-Timestamp: $TS" -H "X-WasaaPay-Signature: $SIG" \
  -H "Idempotency-Key: 4b1e2d3c-9a8b-4c7d-8e6f-5a4b3c2d1e0f" \
  -H "Content-Type: application/json" \
  --data '{
    "wallet_id": "8f3a2b10-1c9e-4c2a-9b2f-3e6d2a1e9c11",
    "destination_type": "mobile_money",
    "destination_ref": "tok_dest_9f21ac",
    "amount": 500000,
    "currency": "KES"
  }'

Response (Payout):

{
  "data": {
    "id": "c9d0e1f2-3a4b-4c5d-8e6f-7a8b9c0d1e2f",
    "wallet_id": "8f3a2b10-1c9e-4c2a-9b2f-3e6d2a1e9c11",
    "batch_id": null,
    "destination_type": "mobile_money",
    "destination_ref": "tok_dest_9f21ac",
    "amount": 500000,
    "currency": "KES",
    "status": "queued",
    "scheduled_at": null,
    "retry_count": 0,
    "failure_reason": null,
    "created_at": "2026-08-15T09:20:00.000Z"
  },
  "error": null,
  "meta": { "request_id": "req_9f2c1a" }
}

Payout status progresses queuedprocessingsuccess / failed, and each transition emits a payout.* webhook. If the source wallet can't cover the amount the request fails immediately with 402 INSUFFICIENT_FUNDS.

Read a single payout with GET /payouts/{id}, or list with GET /payouts?status=&wallet_id= (cursor-paginated).

Bulk payout

Pay many destinations from one wallet in a single call with POST /payouts/bulk. This creates a payout batch and its individual payouts atomically — ideal for disbursing to many sub-merchants at once. Requires an Idempotency-Key.

Request (BulkPayoutCreateRequest):

FieldNotes
wallet_idSource wallet shared by every payout in the batch.
payoutsArray (1–1000). Each item: destination_type, destination_ref, amount, currency.
curl -sS https://sandbox.api.wasaapay.com/v1/payouts/bulk \
  -H "X-WasaaPay-Key: $KEY" -H "X-WasaaPay-Timestamp: $TS" -H "X-WasaaPay-Signature: $SIG" \
  -H "Idempotency-Key: a0b1c2d3-e4f5-4061-8273-8495a6b7c8d9" \
  -H "Content-Type: application/json" \
  --data '{
    "wallet_id": "8f3a2b10-1c9e-4c2a-9b2f-3e6d2a1e9c11",
    "payouts": [
      { "destination_type": "mobile_money", "destination_ref": "tok_dest_aaa", "amount": 250000, "currency": "KES" },
      { "destination_type": "mobile_money", "destination_ref": "tok_dest_bbb", "amount": 300000, "currency": "KES" }
    ]
  }'

Response (PayoutBatch) reports the batch status (processing, completed, or partially_failed) and its individual payouts. Poll the batch with GET /payouts/batches/{id}, or the members with GET /payouts?wallet_id=....

Wallets

Payouts draw from wallets. List them and check balances (read scope):

curl -sS "https://sandbox.api.wasaapay.com/v1/wallets" \
  -H "X-WasaaPay-Key: $KEY" -H "X-WasaaPay-Timestamp: $TS" -H "X-WasaaPay-Signature: $SIG"

curl -sS "https://sandbox.api.wasaapay.com/v1/wallets/{id}/balance" \
  -H "X-WasaaPay-Key: $KEY" -H "X-WasaaPay-Timestamp: $TS" -H "X-WasaaPay-Signature: $SIG"

A wallet balance splits into available_balance (spendable now) and pending_balance (escrowed or uncleared). Only available_balance can be paid out.

Scheduling with a locked FX rate

For a scheduled cross-currency payout, you can lock a rate ahead of time with POST /fx/rate-locks (and inspect live rates with GET /fx/rates?base_currency=&quote_currency=). A rate lock returns a locked_rate and an expires_at; use it to protect a future disbursement from rate movement.