Mobile Money aggregator for African markets. The payer pays from their mobile money wallet (e.g. MTN, Airtel, Orange, M-Pesa). Deposits are processed through PawaPay's hosted Payment Page; payouts are sent directly to the recipient's wallet. Supports Pay-In and Pay-Out.Operations: Pay-In ✓ | Pay-Out ✓ | Webhook ✓
Processing: Asynchronous - Pay-In redirects the payer to PawaPay's hosted Payment Page; final status for both flows arrives via webhook
Payment type: Mobile MoneyCurrency must match the country: PawaPay charges in the local currency of the selected country, and the currency you send must be the currency of that country or the Payment Page is rejected. For example Senegal/Benin/Ivory Coast/Burkina Faso → XOF, Cameroon/Gabon/Congo → XAF, Zambia → ZMW, Rwanda → RWF, Ghana → GHS, Kenya → KES, Uganda → UGX, DR Congo → CDF, Malawi → MWK.
Fields marked with * are required.
Configuration (Mozarto back office)#
These fields are in the locked API Credentials section of the account's Processing Scope tab. Unlock it with your 2FA code first - see Unlock API credentials.
| Field | Description |
|---|
accountName * | Display name for this account |
apiKey * | PawaPay API token (Bearer). Generated in the PawaPay dashboard. Stored per brand, encrypted at rest, and masked in the back office |
siteUrl * | Default return URL - PawaPay redirects the payer here after completing or cancelling a Pay-In on the hosted page. Used when the transaction does not send its own siteUrl |
webhookUrl * | Your HTTPS endpoint for transaction status updates |
comment | Optional free-text note for this configuration |
Pay-In#
POST /v1/api/mozarto/cashier
The cashier call does not move funds directly. It creates a transaction and returns a paymentUrl (PawaPay's hosted Payment Page, valid for 15 minutes). Redirect the payer to that URL, where they choose their mobile money provider and confirm the payment on their phone. The final status arrives later via webhook.Request body#
| Field | Type | Description |
|---|
pspType * | string | Always "PAWAPAY" |
type * | string | Always "PAYIN" |
baseamount * | number | Amount as a number (e.g. 15) |
currency * | string | Currency code. Must match the country's local currency (e.g. "ZMW" for Zambia) |
country * | string | Payer's country - full name, ISO 3166-1 alpha-2, or alpha-3 (e.g. "Zambia", "ZM", "ZMB"). See Country codes. Matched against the account's Processing Scope, and converted to alpha-3 before being sent to PawaPay. Determines the market and available providers |
phone | string | Payer's mobile money number in international format (e.g. 260763456789). Optional - the payer can also enter it on the hosted page |
merchantReference | string | Your reference, echoed back in the response and webhook |
customerMessage | string | Short narration shown to the payer. Validation: only letters, digits, and spaces are kept (any other character is removed) and it is truncated to 22 characters; if fewer than 4 characters remain after cleaning, it is dropped and not sent. When not sent, PawaPay defaults it to the company name |
siteUrl | string | URL the payer returns to after payment. Overrides the configured siteUrl for this transaction; falls back to the configured siteUrl when omitted |
webhookUrl | string | HTTPS endpoint for the status webhook. Overrides the configured webhookUrl for this transaction; falls back to the configured value when omitted |
Example request#
{
"pspType": "PAWAPAY",
"type": "PAYIN",
"baseamount": 15,
"currency": "ZMW",
"country": "ZMB",
"phone": "260763456789",
"merchantReference": "ORDER-4821"
}
Response#
{
"status": "success",
"message": "Successfully",
"isSuccess": true,
"data": {
"transactionId": "64a1f2b3c4d5e6f7a8b9c0d1",
"merchantReference": "ORDER-4821",
"amount": 15,
"transaction_status": "Pending",
"paymentUrl": "https://paywith.pawapay.io/?token=..."
}
}
| Field | Type | Description |
|---|
paymentUrl | string | PawaPay hosted Payment Page URL - redirect the payer here (valid for 15 minutes) |
transactionId | string | Mozarto transaction ID - use it to match the incoming webhook |
merchantReference | string | The merchant reference passed in the request - empty string if not set |
amount | number | Transaction amount as a number |
transaction_status | string | Always "Pending" at this point. The final outcome arrives by webhook |
Pay-Out#
POST /v1/api/mozarto/cashier
Pay-Out is a direct API call - no hosted page and no paymentUrl. Mozarto predicts the mobile money provider from the recipient's phone and sends the payout to their wallet. The final status arrives via webhook.Request body#
| Field | Type | Description |
|---|
pspType * | string | Always "PAWAPAY" |
type * | string | Always "PAYOUT" |
baseamount * | number | Amount as a number (e.g. 15) |
currency * | string | Currency code. Must match the recipient's market local currency |
country * | string | Payer's country - full name, ISO 3166-1 alpha-2, or alpha-3 (e.g. "Germany", "DE", "DEU"). See Country codes. Matched against the account's Processing Scope. Omit it and Mozarto geo-locates the caller's IP instead |
phone * | string | Recipient's mobile money number in international format (e.g. 260763456789). The provider is predicted automatically from this number |
merchantReference | string | Your reference, echoed back in the response and webhook |
customerMessage | string | Short narration shown to the recipient. Validation: only letters, digits, and spaces are kept (any other character is removed) and it is truncated to 22 characters; if fewer than 4 characters remain after cleaning, it is dropped and not sent. When not sent, PawaPay defaults it to the company name |
webhookUrl | string | HTTPS endpoint for the status webhook. Overrides the configured webhookUrl for this transaction; falls back to the configured value when omitted |
Example request#
{
"pspType": "PAWAPAY",
"type": "PAYOUT",
"baseamount": 15,
"currency": "ZMW",
"country": "ZMB",
"phone": "260763456789",
"merchantReference": "PAYOUT-7723"
}
Response#
{
"status": "success",
"message": "Successfully",
"isSuccess": true,
"data": {
"transactionId": "64a1f2b3c4d5e6f7a8b9c0d1",
"merchantReference": "PAYOUT-7723",
"amount": 15,
"transaction_status": "Processing"
}
}
| Field | Type | Description |
|---|
transactionId | string | Mozarto transaction ID - use it to match the incoming webhook |
merchantReference | string | The merchant reference passed in the request - empty string if not set |
amount | number | Transaction amount as a number |
transaction_status | string | Always "Processing" at this point. The final outcome arrives by webhook |
Webhook#
Mozarto calls your webhookUrl when the transaction reaches a final state. PawaPay notifies Mozarto on its own callback URL first; Mozarto then forwards a normalized notification to you. The payload is identical for Pay-In and Pay-Out - it carries no flow indicator. Match transaction_id against the cashier call to distinguish them. Non-final statuses (e.g. still processing) are acknowledged but not forwarded.Payload example#
{
"transaction_id": "64a1f2b3c4d5e6f7a8b9c0d1",
"user_id": "000000000000000000000001",
"status": "success",
"transaction_status": "Approved",
"amount": 15,
"currency": "ZMW",
"merchantReference": "ORDER-4821"
}
| Field | Description |
|---|
transaction_id | Mozarto transaction ID for this transaction. Match it against your stored merchantReference |
user_id | The user ID from the original transaction |
status | Raw settlement result - success or failed. Do not use for business logic |
transaction_status | Normalized Mozarto status - "Approved" or "Failed". Use this for all business logic |
amount | Transaction amount as a number |
currency | Transaction currency |
merchantReference | The merchant reference passed in the original request - empty string if not set |
PawaPay status mapping#
PawaPay's raw statuses are normalized to the Mozarto settlement outcome forwarded above:| PawaPay status | Settlement (status) | Normalized (transaction_status) |
|---|
COMPLETED | success | Approved |
FAILED, REJECTED | failed | Failed |
ACCEPTED, ENQUEUED, PROCESSING, IN_RECONCILIATION, DUPLICATE_IGNORED | pending | Not forwarded (acknowledged only) |
Error codes#
The cashier call returns an error when the transaction cannot be set up. Payment declines are reported later through the webhook with transaction_status: "Failed", not on this call.| HTTP status | Response | When |
|---|
| 400 | message: "PSP Configuration not found. Please check you psp Configuration" | The PawaPay apiKey is missing from the back office configuration |
| 400 | message carries PawaPay's failureReason (e.g. currency/country mismatch, unsupported parameter) | PawaPay rejected the Payment Page or payout. A common cause is a currency that does not match the country's local currency |
| 400 | message: "Unable to determine mobile money provider" | (Pay-Out) PawaPay could not predict a provider from the recipient phone |
For the full list of errorCode values and retryability guidance, see Errors. Modified at 2026-11-02 13:21:02