Guide · 01 / 10

Overview

How WasaaPay works, the response envelope, and every base URL.

WasaaPay is a multi-currency payment aggregator for platforms and marketplaces operating in Africa. This guide is the merchant-facing integration reference for the public /v1 API: collections, hosted checkout, sub-merchant onboarding, wallets, split rules, payouts, webhooks, and FX.

Everything below is grounded in the running services and the OpenAPI contract (docs/wasaapay-openapi.yaml). A fully rendered, offline API reference is in ../api-reference.html — open it in any browser.

Contents

GuideWhat it covers
AuthenticationAPI keys, HMAC request signing (with a worked example), scopes, timestamp skew
Making requestsThe {data,error,meta} envelope, the error-code catalog, idempotency, cursor pagination
CollectionsAccepting a payment two ways: direct POST /collections and hosted checkout sessions; refunds
PayoutsSingle and bulk disbursements
Split & escrowDividing a transaction among recipients; holding funds in escrow
WebhooksRegistering endpoints, the 15 event types, verifying signatures, retries, replay protection
SDKsQuickstarts for Web (@wasaapay/api-client), iOS (WasaaPaySDK), Android (checkout-sdk)
TestingSandbox, the magic phone numbers / card tokens / bank refs, switching to live provider sandboxes
Going liveProduction readiness checklist
API referenceFull rendered OpenAPI (offline, self-contained)

How WasaaPay works

You hold a merchant account. Under it you can onboard sub-merchants (sellers, vendors, riders, individual earners). You collect money from payers over several rails (mobile money, card, bank, USSD, QR), optionally split each collection across recipients and hold shares in escrow, and pay out cleared balances from your wallets. Every state change can be delivered to your server as a signed webhook.

All money amounts in the API are integers in minor units (for example, KES 1,500.00 is 150000). There are no decimals anywhere in the API.

Base URLs

The public API is reached through the API gateway. Every path is prefixed with /v1.

EnvironmentBase URLNotes
Productionhttps://api.wasaapay.com/v1Live money. Real keys, real rails.
Sandboxhttps://sandbox.api.wasaapay.com/v1Test keys; rails are simulated by default (see Testing).
Localhttp://localhost:3000/v1The gateway's default port when you run the stack yourself.

A quick liveness probe (no auth required) confirms which environment you are pointed at:

curl https://sandbox.api.wasaapay.com/v1
# { "data": { "name": "WasaaPay API", "version": "v1", "status": "ok",
#   "resources": ["collections","transactions","sub-merchants","wallets",
#   "split-rules","payouts","webhooks","fx"] },
#   "error": null, "meta": { "request_id": "req_..." } }

Sandbox vs. live

  • Sandbox and production are separate environments with separate API keys. A sandbox key never works against production, and vice versa.
  • In sandbox, the mobile-money, card, and bank rails are served by high-fidelity simulators driven by magic test values (magic phone numbers, card tokens, and account references) — see Testing. No real money moves and no real provider is contacted.
  • You can point specific rails at the real provider sandboxes (Safaricom Daraja, CyberSource) with an environment switch when you want to exercise the genuine provider handshake. That is also covered in Testing.

The shape of every response

Every endpoint returns the same envelope:

{ "data": { }, "error": null, "meta": { "request_id": "req_9f2c1a" } }

On failure, data is null and error carries a machine-readable code. See Making requests for the full contract.

A note on what is and isn't built

This guide documents the API and SDKs as they actually behave in the codebase. Where a capability exists in the HTTP API but a particular SDK hasn't adopted it yet — or where a piece of the platform (for example self-serve signup) isn't built — the relevant page says so explicitly rather than implying it works.