Skip to main content

Transactions

Handling Transaction Webhook Events

Overview

The transaction webhook fires for every transaction that occurs on your wallet. This includes order payments, settlements, and other wallet operations. The webhook sends an HTTP POST request with the transaction details and optionally the related order and customer.

Webhook Event Details

  • Event Trigger: A webhook event is sent when a transaction is created on your wallet.
  • Retry Policy: Up to 3 attempts with exponential backoff if a non-200/201 response is returned.
  • Authentication: Each request carries your secret token verbatim in the Authorization header (no Bearer prefix; the body is not HMAC-signed). Validate it on every request and reject mismatches with 401. See Authorization for the full details, including token rotation.

Payload Reference

The webhook sends a JSON body with the transaction fields at the top level, plus optional order and customer objects.

Transaction Fields

FieldTypeDescription
idstringTransaction ID
amountnumberTransaction amount
feenumberFee charged
totalAmountnumberTotal amount (amount + fee)
descriptionstring | nullTransaction description
paymentTypestringPayment type. Common values: ORDER (order payment), INSTALLMENT (credit repayment), CREDIT (credit disbursement/settlement/fees), WALLET (top-up/transfer), CASHBACK, MEMBERSHIP. Settlements arrive as CREDIT, not a separate SETTLEMENT value.
currencystringKES, UGX, USD
transactionTypestring | nullDirection of the movement on the wallet: TOP_UP (inflow / money received) or DEDUCT (outflow / money sent). For an inbound payment against an order, the seller's wallet receives a TOP_UP.
methodstring | nullMPESA, BANK, WALLET, etc.
walletIdstring | nullWallet account the transaction belongs to
createdAtdatetimeTransaction timestamp
namestring | nullName / label
orderIdstring | nullRelated order ID (if applicable)
statusstringCOMPLETED, PENDING, FAILED. In practice the webhook only fires for COMPLETED transactions — PENDING/FAILED are internal, transient states that are never delivered. You can still safely guard on status == "COMPLETED".

order (optional)

Present when the transaction is linked to an order (orderId is not null). Contains the order information along with shipping and billing details.

FieldTypeDescription
idstringOrder ID
referenceNumberstring | nullUnique order reference
platformReferencestring | nullExternal platform reference
statusstring | nullPENDING, AWAITING_SHIPMENT, DELIVERED, ARCHIVED
paymentStatusstringOrder payment status. Possible values: PENDING (no payment yet), PARTIALLY_PAID (partial amount received), PAID (fully paid), PARTIALLY_REFUNDED, REFUNDED, CREDIT_PROPOSAL_INITIATED (credit created, awaiting borrower signing/deposit).
amountnumberOrder amount
currencystring | nullOrder currency
namestring | nullOrder name
phoneNumberstring | nullCustomer phone number
itemsjson | nullOrder line items
createdAtdatetimeOrder creation timestamp
shippingobject | nullShipping address (see Order Payment docs)
billingobject | nullBilling address (see Order Payment docs)

customer (optional)

Present when the transaction is linked to an order that has a customer/vendor relationship. Customer details as known by the vendor.

FieldTypeDescription
namestring | nullCustomer name
emailstring | nullCustomer email
referencestring | nullCustomer reference in the vendor's system
phoneNumbersstring[]Customer phone numbers

Full Example

{
"id": "01234567-abcd-...",
"amount": 1750.00,
"fee": 0.00,
"totalAmount": 1750.00,
"description": "Order payment",
"paymentType": "ORDER",
"currency": "KES",
"transactionType": "TOP_UP",
"method": "MPESA",
"walletId": "WAL-vendor-...",
"createdAt": "2025-01-15T10:05:00.000Z",
"name": "Payment from John Doe",
"orderId": "01234567-abcd-...",
"status": "COMPLETED",
"order": {
"id": "01234567-abcd-...",
"referenceNumber": "ORD-123456",
"platformReference": null,
"status": "DELIVERED",
"paymentStatus": "PAID",
"amount": 5000.00,
"currency": "KES",
"name": "Order for Widget",
"phoneNumber": "+254712345678",
"items": [
{
"id": "...",
"name": "Widget",
"price": 5000,
"quantity": 1
}
],
"createdAt": "2025-01-15T09:00:00.000Z",
"shipping": {
"id": "01234567-abcd-...",
"firstName": "John",
"lastName": "Doe",
"company": null,
"address1": "123 Main St",
"address2": null,
"landmark": "Near the market",
"city": "Nairobi",
"state": "Nairobi",
"postcode": "00100",
"country": "KE",
"email": "john@example.com",
"phoneNumber": "+254712345678",
"longitude": "36.8219",
"latitude": "-1.2921",
"shippingType": null,
"shippingFee": null,
"isDefault": true,
"createdAt": "2024-08-01T00:00:00.000Z"
},
"billing": {
"id": "01234567-abcd-...",
"firstName": "John",
"lastName": "Doe",
"company": null,
"address1": "123 Main St",
"address2": null,
"city": "Nairobi",
"state": "Nairobi",
"postcode": "00100",
"country": "KE",
"email": "john@example.com",
"phoneNumber": "+254712345678"
}
},
"customer": {
"name": "John Doe",
"email": "john@example.com",
"reference": "CUS018082",
"phoneNumbers": ["+254712345678"]
}
}
note

The order and customer fields are only present when the transaction is linked to an order. For standalone transactions (e.g. settlements, top-ups), these fields will not be included.