WasaaPay API (1.0.0)

Download OpenAPI specification:

WasaaPay Developer Platform: [email protected] URL: https://developers.wasaapay.com License: Proprietary

WasaaPay is a multi-currency payment aggregator for platforms and marketplaces operating in Africa. This specification covers the public merchant-facing API: collections, sub-merchant onboarding, wallets, split rules, payouts, webhooks, and FX. It implements the conventions defined in the WasaaPay Backend Engineering Document (response envelope, idempotency, error catalog) and the schemas defined in the WasaaPay Database Design Document.

Collections

Initiate and inspect payment collections across mobile money, card, and bank rails.

Initiate a payment collection

Creates a new collection (payment) via mobile money, card, or bank rail. Requires an Idempotency-Key header; retrying the same key returns the original result rather than creating a duplicate transaction.

If the collection is settled through a split rule that has an escrow recipient, a releasable escrow hold is minted ASYNCHRONOUSLY after the collection succeeds — so there is no escrow hold ID on this response. Retrieve it with GET /escrow?transaction_id={id} or from the escrow.held webhook (which carries your reference). The hold_in_escrow flag on its own is only a ledger-bucket hint and creates no releasable hold.

Authorizations:
apiKeyAuth
header Parameters
Idempotency-Key
required
string

A client-generated key unique per logical operation. Retried requests with the same key return the original result.

Request Body schema: application/json
required
amount
required
integer >= 1

Minor currency units (e.g. cents).

currency
required
string = 3 characters
rail
required
string (Rail)
Enum: "mobile_money" "card" "bank" "ussd" "qr"
sub_merchant_id
string or null <uuid>
payer_phone
string or null

Required when rail = mobile_money or ussd.

payer_card_token
string or null

Tokenized card reference from the certified card processor. Required when rail = card.

payer_account_ref
string or null

Tokenized bank account reference from the certified provider. Required when rail = bank.

reference
string or null

Merchant-supplied order/reference string, echoed back on the transaction.

hold_in_escrow
boolean
Default: false

When true, the merchant leg of the settlement lands in the pending (escrow) bucket for later release by the split/escrow flow. This flag is only a ledger-bucket hint: on its own it creates NO releasable escrow hold, ID, or lifecycle. A hold you can act on via the /escrow endpoints is created only when the collection's split rule has an escrow recipient.

Responses

Callbacks

Request samples

Content type
application/json
{
  • "amount": 150000,
  • "currency": "KES",
  • "rail": "mobile_money",
  • "payer_phone": "2547XXXXXXXX",
  • "reference": "order_10293"
}

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Callback payload samples

Callback
POST: escrow.held webhook delivery
Content type
application/json
{
  • "id": "string",
  • "type": "escrow.held",
  • "created_at": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

Retrieve collection status

Authorizations:
apiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Refund a collection (partial or full)

Creates a refund transaction against a successful collection. Omit amount (or the request body) to refund the full remaining amount; supply amount in minor currency units for a partial refund. The refund is returned as a transaction with type=refund and parent_transaction_id set to the original collection's ID. Requires an Idempotency-Key header; retrying the same key returns the original result rather than creating a duplicate refund.

Authorizations:
apiKeyAuth
path Parameters
id
required
string <uuid>
header Parameters
Idempotency-Key
required
string

A client-generated key unique per logical operation. Retried requests with the same key return the original result.

Request Body schema: application/json
optional
amount
integer or null >= 1

Refund amount in minor currency units. Omit (or send null) to refund the full remaining amount of the original collection. Must not exceed the original collection amount minus prior refunds.

reason
string or null <= 500 characters

Optional merchant-supplied reason, stored on the refund transaction.

Responses

Request samples

Content type
application/json
{
  • "amount": 50000,
  • "reason": "Customer returned one of two items."
}

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Checkout

Hosted-checkout sessions — payer-facing, capability-token flows where the payer picks the rail.

Open a hosted-checkout session

Creates a payer-facing checkout session for a fixed amount/currency and returns a one-time client_token capability secret. Unlike createCollection (where the merchant picks the rail at creation), the PAYER later chooses a rail from allowed_rails and pays via payCheckoutSession — authorized ONLY by the client_token, never the merchant API key. Requires an Idempotency-Key header; retrying the same key returns the original session (and its token) rather than minting a duplicate.

Authorizations:
apiKeyAuth
header Parameters
Idempotency-Key
required
string

A client-generated key unique per logical operation. Retried requests with the same key return the original result.

Request Body schema: application/json
required
amount
required
integer >= 1

Minor currency units (e.g. cents).

currency
required
string = 3 characters
reference
string or null

Merchant-supplied order/reference string, echoed on the resulting transaction.

sub_merchant_id
string or null <uuid>
allowed_rails
Array of strings (Rail) non-empty
Items Enum: "mobile_money" "card" "bank" "ussd" "qr"

Rails the payer may choose from. Defaults to all rails when omitted.

Responses

Request samples

Content type
application/json
{
  • "amount": 150000,
  • "currency": "KES",
  • "reference": "order_10293",
  • "allowed_rails": [
    ]
}

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Retrieve a checkout session (payer-facing)

Returns the non-sensitive fields the hosted checkout renders. Authorized by the session's client_token (the X-Checkout-Token header or a client_token query parameter), NOT the merchant API key; the client_token is never echoed back. A session past its expires_at is reported as expired.

Authorizations:
checkoutTokenAuth
path Parameters
id
required
string <uuid>
query Parameters
client_token
string

The session's client_token capability secret, as an alternative to the X-Checkout-Token header (see the checkoutTokenAuth security scheme). Supply exactly one of the two.

Responses

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Pay a checkout session (payer-facing)

The payer picks a rail from the session's allowed_rails and supplies the matching payer reference; this creates the underlying collection through the same path as createCollection and marks the session completed. Authorized by the session's client_token (the X-Checkout-Token header or a client_token query parameter). Requires an Idempotency-Key header; a same-key replay returns the original result, and a completed or expired session rejects new pays. Returns the resulting transaction (action_url / ussd_code are populated when a payer challenge is required).

Authorizations:
checkoutTokenAuth
path Parameters
id
required
string <uuid>
query Parameters
client_token
string

The session's client_token capability secret, as an alternative to the X-Checkout-Token header (see the checkoutTokenAuth security scheme). Supply exactly one of the two.

header Parameters
Idempotency-Key
required
string

A client-generated key unique per logical operation. Retried requests with the same key return the original result.

Request Body schema: application/json
required
rail
required
string (Rail)
Enum: "mobile_money" "card" "bank" "ussd" "qr"
payer_phone
string or null

Required when rail = mobile_money or ussd.

payer_card_token
string or null

Tokenized card reference. Required when rail = card.

payer_account_ref
string or null

Tokenized bank account reference. Required when rail = bank.

Responses

Request samples

Content type
application/json
{
  • "rail": "mobile_money",
  • "payer_phone": "2547XXXXXXXX"
}

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Transactions

Search and inspect transaction records.

List transactions

Cursor-paginated list of transactions, filterable by status, rail, and date range.

Authorizations:
apiKeyAuth
query Parameters
cursor
string

Opaque pagination cursor from a previous response's meta.next_cursor.

limit
integer [ 1 .. 100 ]
Default: 50
status
string (TransactionStatus)
Enum: "pending" "success" "failed" "reversed"
rail
string (Rail)
Enum: "mobile_money" "card" "bank" "ussd" "qr"
date_from
string <date-time>
date_to
string <date-time>

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "error": {
    },
  • "meta": {
    }
}

Retrieve a transaction by ID

Authorizations:
apiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Sub-Merchants

Onboard and manage sellers, vendors, riders, and individual earners.

Onboard a new sub-merchant

Programmatically onboard a seller, vendor, rider, or individual earner. Triggers a KYC case per the assigned kyc_tier.

Authorizations:
apiKeyAuth
header Parameters
Idempotency-Key
required
string

A client-generated key unique per logical operation. Retried requests with the same key return the original result.

Request Body schema: application/json
required
display_name
required
string
type
required
string
Enum: "individual" "business"
country
required
string = 2 characters
kyc_tier
string (KycTier)
Enum: "tier_1" "tier_2"
object

Responses

Request samples

Content type
application/json
{
  • "display_name": "Jane's Boutique",
  • "type": "individual",
  • "country": "KE",
  • "kyc_tier": "tier_1",
  • "contact": {}
}

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

List sub-merchants

Authorizations:
apiKeyAuth
query Parameters
cursor
string

Opaque pagination cursor from a previous response's meta.next_cursor.

limit
integer [ 1 .. 100 ]
Default: 50
kyc_status
string (KycStatus)
Enum: "pending" "verified" "rejected" "needs_review"

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "error": {
    },
  • "meta": {
    }
}

Retrieve a sub-merchant by ID

Authorizations:
apiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Team

Dashboard team management — the users belonging to a merchant.

List the merchant's dashboard users (team roster)

Returns every dashboard user under the authenticated merchant. The listing is always scoped to the caller's merchant, so one merchant can never enumerate another merchant's users. Requires the team:read permission (admin, finance, read_only, or compliance roles).

Authorizations:
apiKeyAuth

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "error": {
    },
  • "meta": {
    }
}

Wallets

Multi-currency wallet balances.

List wallets

Returns every wallet owned by the authenticated merchant (one per currency) or its sub-merchants.

Authorizations:
apiKeyAuth
query Parameters
cursor
string

Opaque pagination cursor from a previous response's meta.next_cursor.

limit
integer [ 1 .. 100 ]
Default: 50
owner_id
string <uuid>

Filter to a specific merchant or sub-merchant owner.

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "error": {
    },
  • "meta": {
    }
}

Retrieve current available and pending balance

Authorizations:
apiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Split Rules

Configure how a transaction's value is divided among recipients.

Create a split rule

Defines how a transaction's value is divided among recipients, with optional escrow per recipient.

Authorizations:
apiKeyAuth
Request Body schema: application/json
required
name
required
string
rule_type
required
string (SplitRuleType)
Enum: "percentage" "flat" "tiered"
currency
string or null = 3 characters

Required for flat (fixed-amount) rules — the ISO 4217 currency the flat recipient values are denominated in. Omit for percentage rules.

required
Array of objects (SplitRuleRecipient) non-empty

Responses

Request samples

Content type
application/json
{
  • "name": "Standard marketplace split",
  • "rule_type": "percentage",
  • "recipients": [
    ]
}

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

List split rules

Authorizations:
apiKeyAuth
query Parameters
cursor
string

Opaque pagination cursor from a previous response's meta.next_cursor.

limit
integer [ 1 .. 100 ]
Default: 50
active
boolean

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "error": {
    },
  • "meta": {
    }
}

Retrieve a split rule by ID

Authorizations:
apiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Escrow

Inspect and settle escrow holds — release to the recipient or return to the merchant.

List escrow holds

Returns this merchant's escrow holds, newest first. A releasable hold is created ASYNCHRONOUSLY after a collection whose split rule has an escrow recipient succeeds, so the hold ID is not present on the collection response — this endpoint (filtered by transaction_id) and the escrow.held webhook are how you retrieve it. Filter by transaction_id, status, and/or sub_merchant_id (the escrowed recipient).

Authorizations:
apiKeyAuth
query Parameters
transaction_id
string <uuid>

Only holds minted from this collection transaction.

status
string (EscrowHoldStatus)
Enum: "held" "partially_released" "released" "returned" "disputed"

Lifecycle state of an escrow hold.

sub_merchant_id
string <uuid>

Only holds whose recipient is this sub-merchant.

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "error": {
    },
  • "meta": {
    }
}

Retrieve an escrow hold by ID

Returns one escrow hold owned by this merchant. A hold that belongs to a different merchant (or does not exist) returns 404 RESOURCE_NOT_FOUND — the API never reveals the existence of another merchant's hold.

Authorizations:
apiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Release an escrow hold (partial or full)

Releases escrowed funds to the hold's recipient (the escrowed sub-merchant). Omit amount (or the whole body) to release the full remaining amount; supply amount in minor currency units for a partial, per-milestone release. A partial release moves the hold to partially_released and leaves remaining_amount held; a release that clears the remainder moves it to released. This endpoint is also the programmatic trigger for a delivery_confirmation hold. Requires an Idempotency-Key header (a missing key is rejected 422 VALIDATION_ERROR); retrying the same key returns the original result rather than releasing again.

Authorizations:
apiKeyAuth
path Parameters
id
required
string <uuid>
header Parameters
Idempotency-Key
required
string

A client-generated key unique per logical operation. Retried requests with the same key return the original result.

Request Body schema: application/json
optional
amount
integer >= 1

Partial amount to release, in minor currency units. Omit to release the full remaining amount. Must not exceed remaining_amount.

reason
string or null <= 2000 characters

Optional merchant note, persisted on the hold.

reference
string or null <= 140 characters

Optional merchant reference, persisted on the hold and echoed on escrow webhooks.

Responses

Callbacks

Request samples

Content type
application/json
{
  • "amount": 2000,
  • "reason": "delivery confirmed",
  • "reference": "order-123"
}

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Callback payload samples

Callback
POST: escrow.released webhook delivery
Content type
application/json
{
  • "id": "string",
  • "type": "escrow.held",
  • "created_at": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

Return an escrow hold to the merchant (partial or full)

Returns escrowed funds to the MERCHANT's available balance (NOT to the original card / mobile-money payer). The merchant then issues any refund to the payer out of band (for example via POST /collections/{id}/refunds). Omit amount (or the whole body) to return the full remaining amount; supply amount in minor currency units for a partial return. Requires an Idempotency-Key header (a missing key is rejected 422 VALIDATION_ERROR); retrying the same key returns the original result rather than returning again.

Authorizations:
apiKeyAuth
path Parameters
id
required
string <uuid>
header Parameters
Idempotency-Key
required
string

A client-generated key unique per logical operation. Retried requests with the same key return the original result.

Request Body schema: application/json
optional
amount
integer >= 1

Partial amount to return, in minor currency units. Omit to return the full remaining amount. Must not exceed remaining_amount.

reason
string or null <= 2000 characters

Optional merchant note, persisted on the hold.

Responses

Callbacks

Request samples

Content type
application/json
{
  • "amount": 1500,
  • "reason": "refund agreed"
}

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Callback payload samples

Callback
POST: escrow.returned webhook delivery
Content type
application/json
{
  • "id": "string",
  • "type": "escrow.held",
  • "created_at": "2019-08-24T14:15:22Z",
  • "data": {
    }
}

Payouts

Single and bulk disbursements.

Initiate a single payout

Authorizations:
apiKeyAuth
header Parameters
Idempotency-Key
required
string

A client-generated key unique per logical operation. Retried requests with the same key return the original result.

Request Body schema: application/json
required
wallet_id
required
string <uuid>
destination_type
required
string (DestinationType)
Enum: "mobile_money" "bank" "card"
destination_ref
required
string

Tokenized destination account reference obtained via the payout-destination tokenization flow.

amount
required
integer >= 1
currency
required
string = 3 characters
scheduled_at
string or null <date-time>

Responses

Request samples

Content type
application/json
{
  • "wallet_id": "8f3a2b10-1c9e-4c2a-9b2f-3e6d2a1e9c11",
  • "destination_type": "mobile_money",
  • "destination_ref": "tok_dest_9f21ac",
  • "amount": 500000,
  • "currency": "KES"
}

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

List payouts

Authorizations:
apiKeyAuth
query Parameters
cursor
string

Opaque pagination cursor from a previous response's meta.next_cursor.

limit
integer [ 1 .. 100 ]
Default: 50
status
string (PayoutStatus)
Enum: "queued" "processing" "success" "failed"
wallet_id
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "error": {
    },
  • "meta": {
    }
}

Initiate a batch of payouts

Creates a payout_batch and its individual payouts in a single call, for paying many sub-merchants at once.

Authorizations:
apiKeyAuth
header Parameters
Idempotency-Key
required
string

A client-generated key unique per logical operation. Retried requests with the same key return the original result.

Request Body schema: application/json
required
wallet_id
required
string <uuid>

Source wallet shared by every payout in the batch.

required
Array of objects [ 1 .. 1000 ] items

Responses

Request samples

Content type
application/json
{
  • "wallet_id": "d9a8fbfc-848f-43a2-9168-911ed3a04a48",
  • "payouts": [
    ]
}

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Retrieve a payout batch by ID

Returns the batch, its aggregate status, and its individual payouts.

Authorizations:
apiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Retrieve a payout by ID

Authorizations:
apiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Webhooks

Register endpoints to receive event notifications.

Register a webhook endpoint

Authorizations:
apiKeyAuth
Request Body schema: application/json
required
url
required
string <uri>
subscribed_events
required
Array of strings (WebhookEventType) non-empty
Items Enum: "transaction.pending" "transaction.success" "transaction.failed" "transaction.reversed" "payout.queued" "payout.processing" "payout.success" "payout.failed" "escrow.held" "escrow.released" "escrow.returned" "dispute.opened" "dispute.resolved" "kyc.verified" "kyc.rejected"

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

List registered webhook endpoints

Authorizations:
apiKeyAuth

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "error": {
    },
  • "meta": {
    }
}

Remove a webhook endpoint

Authorizations:
apiKeyAuth
path Parameters
id
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "data": { },
  • "error": {
    },
  • "meta": {
    }
}

FX

Foreign exchange rates and rate locks.

Retrieve current FX rates

Authorizations:
apiKeyAuth
query Parameters
base_currency
required
string = 3 characters
quote_currency
required
string = 3 characters

Responses

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}

Lock an FX rate for a scheduled future payout

Authorizations:
apiKeyAuth
Request Body schema: application/json
required
base_currency
required
string = 3 characters
quote_currency
required
string = 3 characters

Responses

Request samples

Content type
application/json
{
  • "base_currency": "str",
  • "quote_currency": "str"
}

Response samples

Content type
application/json
{
  • "data": {
    },
  • "error": {
    },
  • "meta": {
    }
}