Mobile Money Charge

This guide explains the end-to-end flow to charge a customer via mobile money, verify the transaction status, and handle webhook notifications.

1 - Initiate Charge

FieldTypeRequiredDescription
amountNumberAmount to be charged (e.g. 1500)
currencyStringCurrency code (e.g. KES)
externalReferenceStringYour own unique ID for reconciliation (e.g. invoice number)
phoneNumberStringRecipient phone number in E.164 without the plus (e.g. 254719624551)
momoOperatorIdStringMobile-money operator ID (required for all currencies except KES)
walletCurrencyStringCurrency to settle funds into (defaults to the value of currency)

Note: If currency is not KES, you must include momoOperatorId.

To charge a customer, collect the required payment information and send it to the initiate mobile money charge endpoint.

To retrieve the mobile momoOperatorId, call the get mobile money providers endpoint.

Example Request:

{  
  "amount": 100,  
  "currency": "KES",  
  "externalReference": "order_12345",  
  "phoneNumber": "254719624551"  
}
{  
  "amount": 100,  
  "currency": "KES",  
  "externalReference": "order_12345",  
  "phoneNumber": "254719624551",
  "momoOperatorId": "airtel"
}

Below is an example of the response:

{
  "success": true,
  "message": "Initiated request.",
  "transactionId": "123456789"
}

Note:Use the returned transactionId (or your externalReference) to track status.

2 - Get Transaction Status

Always verify the payment status before providing value to your customer. Use the get transaction endpoint with either:

  • The transactionId from the charge response or
  • Your externalReference

Here's an example of charge verification and response:

{
  "success": true,
  "data": {
    "transactionId": 'lBK9bMny2gs4hLsG3XGq',
    "amount": 25,
    "type": 'deposit',
    "currency": 'KES',
    "senderCurrency": 'KES',
    "senderAmount": 25,
    "receiverCurrency": 'KES',
    "receiverAmount": 25,
    "chargeStatus": 'successful',
    "status": 'SUCCESSFUL',
    "method": 'momo',
    "note": 'TFH174NFSJ Confirmed. Ksh25.00 sent to Honeycoin 722223344 on 17/06/25 at 1:17 AM. Transaction cost, Ksh0.00.', // optional
    "fullTimestamp": '2025-06-17T01:16:44+03:00',
    "externalReference": "test",
    "thirdPartyReference": 'TFH174NFSJ', // optional
    "phoneNumber": '254722416788',
    "stepRequired": "otp", // Only when a step is required it can be: otp/redirect.
    "redirectUrl": "https://test.com"
  }
}
{
  "success": true,
  "data": {
    "transactionId": '173675873400000038',
    "amount": 102,
    "type": 'deposit',
    "currency": 'KES',
    "senderCurrency": 'KES',
    "senderAmount": 102,
    "receiverCurrency": 'KES',
    "receiverAmount": 102,
    "chargeStatus": 'failed',
    "status": 'FAILED',
    "method": 'mpesa',
    "note": 'STK Prompt Time Out',
    "fullTimestamp": '2025-01-14T12:13:59+00:00',
    "externalReference": '173675873400000038',
    "phoneNumber": '254700000001'
  }
}

3 - Handle Webhooks

Configure webhooks to receive real-time transaction updates instead of polling the status endpoint.

Setup

  1. Configure your webhook URL in your dashboard account.
  2. Implement webhook endpoint security and validation.
  3. Handle the webhook notifications in your application

Here's a sample of the webhook response:

{
  "event": "transaction_created",
  "data": {
    "transactionId": "BeOfXV1NVIcZlsSVeQAF",
    "status": "pending",
    "type": "deposit",
    "externalReference": "test",
  },
  "timestamp": "2024-10-03T16:33:14.600Z"
}
{
  "event": "transaction_updated",
  "data": {
    "transactionId": "BeOfXV1NVIcZlsSVeQAF",
    "status": "successful",
    "type": "deposit",
    "externalReference": "test",
    "method": "momo",
    "thirdPartReference": "TEQTEYRU" // optional. e.g Mpesa reference
  },
  "timestamp": "ISO-8601 timestamp"
}
{
  "event": "transaction_updated",
  "data": {
    "transactionId": "BeOfXV1NVIcZlsSVeQAF",
    "status": "failed",
    "type": "deposit",
    "externalReference": "test",
    "method": "momo",
    "note": "User could not be reached by stk" // optional. e.g error message
  },
  "timestamp": "ISO-8601 timestamp"
}
{
  "event": "transaction_updated",
  "data": {
    "transactionId": "BeOfXV1NVIcZlsSVeQAF",
    "status": "pending",
    "type": "deposit",
    "externalReference": "test",
    "method": "momo",
    "stepRequired": "otp"
  },
  "timestamp": "ISO-8601 timestamp"
}
{
  "event": "transaction_updated",
  "data": {
    "transactionId": "BeOfXV1NVIcZlsSVeQAF",
    "status": "pending",
    "type": "deposit",
    "externalReference": "test",
    "method": "momo",
    "stepRequired": "redirect",
    "redirectUrl": "https://test.com/"
  },
  "timestamp": "ISO-8601 timestamp"
}
  • If stepRequired is otp: Prompt the customer to enter the OTP they received on their phone.
  • If stepRequired is redirect: Redirect the customer to the provided redirectUrl to complete the payment.

4 - Validate OTP

If a webhook with stepRequired: 'otp' is received, you need to collect the OTP from the user and send it to the validate otp endpoint to proceed with the transaction.

URL: /api/b2b/fiat/deposit/:transactionId/validate-otp

FieldTypeRequiredDescription
otpStringThe one-time password provided by the user

Example Response:

{  
  "success": true,  
  "message": "OTP validated successfully.",  
  "transactionId": "123456789"  
}

Note: After a successful OTP validation, the transaction will continue processing in the background. You will receive a final SUCCESSFUL or FAILED webhook when the transaction is complete.