Take a payment by mobile money push
const url = 'https://cybqa.pesapal.com/pesapalv3/api/transactions/stk';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"id":"example","currency":"KES","amount":1,"description":"example","callback_url":"example","cancellation_url":"example","notification_id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","billing_address":{"email_address":"example","phone_number":"example","first_name":"example","middle_name":"example","last_name":"example","country_code":"example","line_1":"example","line_2":"example","city":"example","state":"example","postal_code":"example","zip_code":"example"},"branch":"example","redirect_mode":"TOP_WINDOW","account_number":"example","subscription_details":{"start_date":"01-01-2027","end_date":"01-01-2028","frequency":"DAILY"},"msisdn":"example","payment_method":"MpesaKE"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://cybqa.pesapal.com/pesapalv3/api/transactions/stk \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "id": "example", "currency": "KES", "amount": 1, "description": "example", "callback_url": "example", "cancellation_url": "example", "notification_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "billing_address": { "email_address": "example", "phone_number": "example", "first_name": "example", "middle_name": "example", "last_name": "example", "country_code": "example", "line_1": "example", "line_2": "example", "city": "example", "state": "example", "postal_code": "example", "zip_code": "example" }, "branch": "example", "redirect_mode": "TOP_WINDOW", "account_number": "example", "subscription_details": { "start_date": "01-01-2027", "end_date": "01-01-2028", "frequency": "DAILY" }, "msisdn": "example", "payment_method": "MpesaKE" }'Sends an M-Pesa or Airtel Money PIN prompt directly to a phone number, with no hosted payment page and no redirect. This is the endpoint for in-app payment flows.
The request is an ordinary order body plus msisdn and
payment_method. The response carries no redirect_url, and instead
returns the business_number and account_number the customer can pay
manually if the prompt does not arrive. Both are worth showing them.
Note that merchant_reference in the response does not echo the id
that was sent, and msisdn is not validated for shape.
Unlike every other operation, a missing required field here returns
HTTP 400 with an ASP.NET model-state object rather than the usual
error envelope. An invalid payment_method value still returns HTTP 200
with the ordinary error shape.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
Your own reference, which must be unique. Values between 51 and 150 characters fail with a generic decline that does not mention length.
Validated against a lookup table. Acceptance at submission does not guarantee a customer can complete a payment in that currency.
Shown to the customer. No maximum length was enforced.
Plain HTTP is accepted.
An ipn_id from registerIpn.
The object is mandatory on an order, but every field within it is optional. An empty object is accepted.
object
ISO 3166-1 alpha-2, for example KE.
Your identifier for a subscriber. Send with subscription_details.
object
Format dd-MM-yyyy. ISO 8601 is rejected. Must be in the future.
Format dd-MM-yyyy. The range is not validated, so an end date
before the start date is accepted.
Case insensitive.
The phone number to prompt. Not validated for shape, so an unroutable number is accepted and an order is still created.
Other values, including MpesaTZ, MtnUG, TigoTZ and
VodacomTZ, are rejected with Invalid Payment method provided.
Responses
Section titled “Responses”Returned for a successful push and for an invalid field value.
object
The paybill or shortcode the customer can pay manually if the prompt
does not arrive. 220222 for M-Pesa Kenya, AIRTELKE and
AIRTELUG for Airtel.
The account reference to quote alongside business_number.
A generated value that does not echo the id you sent. The order
is stored under your id regardless: looking the transaction up
afterwards returns it, and the IPN carries it. Ignore this field and
key your records on order_tracking_id.
Always null. There is no hosted page in this flow.
An application error, returned with HTTP 200.
object
A populated error. Every field is present and non-null.
object
Mostly snake_case, but not consistently: InvalidIpnId is
PascalCase. Compare exactly.
Multiple validation errors arrive pipe-delimited in one string, and malformed JSON produces leading empty segments.
Usually "500". Never matches the HTTP status.
Example
{ "error": { "error_type": "api_error" }}A required field was missing.
ASP.NET model validation, returned with HTTP 400 by
directMobileMoneyStk when a required field is absent. No other
operation returns this shape.
object
object
Example
{ "message": "The request is invalid.", "modelState": { "minifiedMobileCheckoutDTO.Msisdn": [ "Phone Number is required" ] }}