Skip to main content

Transactions

Handling Transaction Webhook Events​

Overview​

The TRANSACTION webhook fires for every completed money movement on your Sevi wallet: order payments credited to you, daily settlements to your bank, refunds and other wallet operations. Use it for your "money received / money paid out" bookkeeping.

Webhook Event Details​

  • Event Trigger: Sent when a transaction on your wallet is completed.
  • 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.
  • Idempotency: A transaction can be delivered more than once (retries). Use id to deduplicate.

Recognising an order payment​

In the standard Integration flow the buyer's payment is held by Sevi until delivery is confirmed. You receive exactly one transaction for the order, at the moment your wallet is credited:

FieldValue
transactionTypeTOP_UP
statusCOMPLETED
paymentTypeORDER
orderIdthe order id (the order object is included)
hold.wasHeldtrue — the money was held until delivery
hold.reasonDELIVERY_CONFIRMATION
hold.releasedAtwhen delivery was confirmed and the money was credited
amountthe (delivered) order amount

Use these fields — not the description — in your logic. The description is human-readable text (e.g. Payment for order: D123456) and may change.

Recognising a settlement​

The daily payout of your wallet balance to your bank account, M-Pesa paybill or till:

FieldValue
transactionTypeDEDUCT
paymentTypeMONEY_REQUEST
methodBANK, MPESA_PAYBILL or MPESA_TILL (payout fees arrive as a separate DEDUCT with method FEE)
orderIdnull

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 | nullHuman-readable description. Do not parse it.
paymentTypestringWhat the movement is for. Common values: ORDER (order payment), MONEY_REQUEST (settlement / payout), WALLET (top-up / transfer), INSTALLMENT (credit repayment), CREDIT, CASHBACK, MEMBERSHIP.
currencystringKES, UGX, USD
transactionTypestring | nullDirection on your wallet: TOP_UP (money in) or DEDUCT (money out).
methodstring | nullSEVI (internal, e.g. order credit), MPESA, BANK, MPESA_PAYBILL, MPESA_TILL, FEE, …
walletIdstring | nullWallet account the transaction belongs to (yours)
createdAtdatetimeWhen the transaction was created. For a held order payment this is when the buyer paid; use hold.releasedAt for when you were credited.
namestring | nullName / label
orderIdstring | nullRelated order ID (if applicable)
statusstringCOMPLETED. The webhook is only sent for completed transactions; you can safely guard on status == "COMPLETED".
holdobjectEscrow information, see below

hold​

Always present.

FieldTypeDescription
wasHeldbooleantrue when the money was held by Sevi before it was credited (order payments awaiting delivery). false for ordinary transactions.
reasonstring | nullWhy it was held: DELIVERY_CONFIRMATION (until the delivery was confirmed) or SETTLEMENT_CONFIRMATION. null when wasHeld is false.
releasedAtdatetime | nullWhen the hold was released and your wallet was credited.

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 | nullYour invoice number
statusstring | nullPENDING, AWAITING_SHIPMENT, DELIVERED, ARCHIVED, CANCELLED, REFUNDED, RETURNED
paymentStatusstringPENDING, PARTIALLY_PAID, CREDIT_PROPOSAL_INITIATED, PAID, PARTIALLY_REFUNDED, REFUNDED
amountnumberOrder amount
currencystring | nullOrder currency
namestring | nullOrder name
phoneNumberstring | nullCustomer phone number
itemsjson | nullOrder line items
createdAtdatetimeOrder creation timestamp
shippingobject | nullShipping address (see Order webhook)
billingobject | nullBilling address (see Order webhook)

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​

An order payment credited after the buyer confirmed delivery:

{
"id": "01234567-abcd-...",
"amount": 5000.00,
"fee": 0.00,
"totalAmount": 5000.00,
"description": "Payment for order: D123456",
"paymentType": "ORDER",
"currency": "KES",
"transactionType": "TOP_UP",
"method": "SEVI",
"walletId": "WAL-vendor-...",
"createdAt": "2025-01-15T09:12:41.000Z",
"name": "Jane Wanjiku",
"orderId": "0b9f6f7e-5a8c-4a57-9a52-3c2f0e1d9a11",
"status": "COMPLETED",
"hold": {
"wasHeld": true,
"reason": "DELIVERY_CONFIRMATION",
"releasedAt": "2025-01-16T14:03:10.000Z"
},
"order": {
"id": "0b9f6f7e-5a8c-4a57-9a52-3c2f0e1d9a11",
"referenceNumber": "D123456",
"platformReference": "INV-1001",
"status": "DELIVERED",
"paymentStatus": "PAID",
"amount": 5000.00,
"currency": "KES",
"name": "Order for Maize flour",
"phoneNumber": "+254712345678",
"items": [
{
"name": "Maize flour 2kg",
"unitPrice": 2500,
"quantity": 2
}
],
"createdAt": "2025-01-15T09:00:00.000Z",
"shipping": {
"firstName": "Jane",
"lastName": "Wanjiku",
"address1": "Kenyatta Avenue 12",
"city": "Nairobi",
"country": "KE",
"phoneNumber": "+254712345678"
},
"billing": {
"firstName": "Jane",
"lastName": "Wanjiku",
"address1": "Kenyatta Avenue 12",
"city": "Nairobi",
"country": "KE",
"phoneNumber": "+254712345678"
}
},
"customer": {
"name": "Jane Wanjiku",
"email": null,
"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 are not included.