Skip to content

Pesapal (API 3.0) · Error reference

Pesapal API 3.0 Errors and Payment Statuses Explained

Pesapal API 3.0 reports a payment as one of four statuses (COMPLETED, FAILED, REVERSED or INVALID) from GetTransactionStatus, and reports rejected requests with an error object of type, code and message.

Have a response you don't understand? Paste it into the Payment Error Code Lookup and it finds every known code inside.

How Pesapal reports errors

  • Rejected requests come back as JSON with an error object: {"type": "...", "code": "...", "message": "..."}. Pesapal's docs don't publish a list of the codes, so log the whole object.
  • The payment outcome comes only from GET /api/Transactions/GetTransactionStatus: payment_status_description and status_code (0 INVALID, 1 COMPLETED, 2 FAILED, 3 REVERSED).
  • The callback redirect (OrderNotificationType CALLBACKURL) and the IPN (IPNCHANGE) never carry the status. They only tell you which order to look up.

How sure is this?

Each code below is labelled by how well it is documented. All 10 codes on this page come from Pesapal's own documentation, linked under each one. Log the full response anyway: messages change.

Payment outcomes

The status values that tell you whether the customer paid.

COMPLETED Paid (status_code 1)

PaidSeen in: Status / callbackRetry: not neededIn the provider's docs

GetTransactionStatus returns payment_status_description Completed and status_code 1: the customer paid.

What to do

  • Check that merchant_reference is your order and that amount and currency match, then fulfil the order once.
  • Store confirmation_code: a refund needs it.

Sources: Pesapal API 3.0 docs: GetTransactionStatus

INVALID Not paid (status_code 0)

WaitingSeen in: Status / callbackRetry: No, fix the cause firstIn the provider's docs

status_code 0. Pesapal's docs list INVALID as one of the four statuses without defining it. It is not COMPLETED, so the order is not paid.

What to do

  • Don't fulfil. Check again after the IPN or from a scheduled job, and expire the order if it never completes.

The docs give no definition for INVALID beyond the name and code. Treating it as "not paid yet, keep checking" is the safe reading: never fulfil on it, and never fail an order on it alone.

Sources: Pesapal API 3.0 docs: GetTransactionStatus

IPNCHANGE IPN: a status changed

Not known yetSeen in: CallbackRetry: Check the status before retryingIn the provider's docs

OrderNotificationType on an IPN call. Pesapal calls your registered IPN URL (GET or POST, as you registered it) with OrderTrackingId and OrderMerchantReference when a payment status changes. It does not include the status.

How to fix it

  • Call GetTransactionStatus with the OrderTrackingId and settle from that. Never mark an order paid from the IPN itself.

Sources: Pesapal API 3.0 docs: SubmitOrderRequest

CALLBACKURL Customer redirected back

Not known yetSeen in: CallbackRetry: Check the status before retryingIn the provider's docs

OrderNotificationType when Pesapal sends the customer back to your callback_url, with OrderTrackingId and OrderMerchantReference in the query string. It does not include the status.

How to fix it

  • Call GetTransactionStatus before showing "paid". If it isn't COMPLETED yet, show a "confirming your payment" page.

Sources: Pesapal API 3.0 docs: SubmitOrderRequest

Failure reasons

Why a payment did not go through.

FAILED Payment failed (status_code 2)

Not paidSeen in: Status / callbackRetry: Yes, as a new requestIn the provider's docs

status_code 2: the payment did not go through. description says why.

How to fix it

  • Mark the order unpaid and let the customer try again with a new order request (a new id).

What to tell the customer

Your payment didn't go through. Please try again.

Sources: Pesapal API 3.0 docs: GetTransactionStatus

REVERSED Payment reversed (status_code 3)

Not paidSeen in: Status / callbackRetry: No, fix the cause firstIn the provider's docs

status_code 3: a payment was reversed.

How to fix it

  • If you had marked the order paid, mark it reversed and stop or recover the goods or service. Look at the payment in your Pesapal dashboard.

Sources: Pesapal API 3.0 docs: GetTransactionStatus

Request errors

Returned straight away when the API rejects your request.

error object Request rejected (type, code, message)

Request errorSeen in: API responseRetry: No, fix the cause firstIn the provider's docs

When a call fails, Pesapal answers with a JSON error object of type, code and message. The docs don't list the codes.

How to fix it

  • Log the whole object. Check the token (it lasts at most 5 minutes), the notification_id (register the IPN in the same environment), and that your order id uses only letters, numbers and - _ . : (max 50).

Successful GetTransactionStatus answers can also contain an error object whose fields are all null. Treat it as an error only when code or message is set.

Sources: Pesapal API 3.0 docs: API reference · Pesapal API 3.0 docs: authentication · Pesapal API 3.0 docs: SubmitOrderRequest

RefundRequest 200 Refund request received

WaitingSeen in: API responseRetry: No, fix the cause firstIn the provider's docs

RefundRequest answered 200: the refund was received and is being processed. It is not complete: the merchant must approve it.

What to do

  • Approve the refund in Pesapal. Only one refund is allowed per payment.

Sources: Pesapal API 3.0 docs: RefundRequest

RefundRequest 500 Refund rejected

Request errorSeen in: API responseRetry: No, fix the cause firstIn the provider's docs

RefundRequest answered 500: the refund was rejected. The message says why.

How to fix it

  • Only COMPLETED payments can be refunded, once, in the same currency, up to the amount paid. Mobile money payments can only be refunded in full; cards in full or in part. Use the confirmation_code from GetTransactionStatus.

Sources: Pesapal API 3.0 docs: RefundRequest

CancelOrder 500 Order not cancelled

Request errorSeen in: API responseRetry: No, fix the cause firstIn the provider's docs

CancelOrder answered 500: the order could not be cancelled.

How to fix it

  • Only failed or pending payments can be cancelled, and only once. A processed payment can't be cancelled: refund it instead.

Sources: Pesapal API 3.0 docs: order cancellation

Handle every code in your code

Map each outcome code to one of four actions. Unknown codes fall back to "check the status", so a new or misspelled code never fulfils or cancels an order by accident.

// What to do with each Pesapal (API 3.0) outcome code.
// Unknown codes fall back to "check_status": never fulfil or fail an order on a code you don't recognise.
const ACTIONS = {
  "COMPLETED": "fulfil", // Paid (status_code 1)
  "FAILED": "mark_failed", // Payment failed (status_code 2)
  "INVALID": "wait", // Not paid (status_code 0)
  "REVERSED": "mark_failed", // Payment reversed (status_code 3)
  "IPNCHANGE": "check_status", // IPN: a status changed
  "CALLBACKURL": "check_status", // Customer redirected back
};

export function pesapalNextStep(code) {
  const key = String(code ?? "").trim().toUpperCase().replace(/[\s-]+/g, "_");
  return ACTIONS[key] ?? "check_status";
}

// Example:
// switch (pesapalNextStep(status.payment_status_description)) {
//   case "fulfil":       // verify amount, then mark the order paid (idempotently)
//   case "mark_failed":  // mark unpaid, show a retry button
//   case "wait":         // do nothing yet; a callback or your poller will follow
//   case "check_status": // ask the status endpoint before deciding
// }

Frequently asked questions

What does Pesapal status INVALID mean?

INVALID (Not paid (status_code 0)): status_code 0. Pesapal's docs list INVALID as one of the four statuses without defining it. It is not COMPLETED, so the order is not paid. Don't fulfil. Check again after the IPN or from a scheduled job, and expire the order if it never completes.

What does Pesapal status IPNCHANGE mean?

IPNCHANGE (IPN: a status changed): OrderNotificationType on an IPN call. Pesapal calls your registered IPN URL (GET or POST, as you registered it) with OrderTrackingId and OrderMerchantReference when a payment status changes. It does not include the status. Call GetTransactionStatus with the OrderTrackingId and settle from that. Never mark an order paid from the IPN itself.

What does Pesapal status REVERSED mean?

REVERSED (Payment reversed (status_code 3)): status_code 3: a payment was reversed. If you had marked the order paid, mark it reversed and stop or recover the goods or service. Look at the payment in your Pesapal dashboard.

What does Pesapal error error object mean?

error object (Request rejected (type, code, message)): When a call fails, Pesapal answers with a JSON error object of type, code and message. The docs don't list the codes. Log the whole object. Check the token (it lasts at most 5 minutes), the notification_id (register the IPN in the same environment), and that your order id uses only letters, numbers and - _ . : (max 50).

What does Pesapal error RefundRequest 500 mean?

RefundRequest 500 (Refund rejected): RefundRequest answered 500: the refund was rejected. The message says why. Only COMPLETED payments can be refunded, once, in the same currency, up to the amount paid. Mobile money payments can only be refunded in full; cards in full or in part. Use the confirmation_code from GetTransactionStatus.

Integration guides

Full Pesapal integrations that handle these codes: Pesapal API 3.0 in Node.js, Laravel, Django, Next.js, Pesapal Request Builder.

Checked November 2026. Error codes change. If you see one that's missing or wrong, please let me know.

Need this built for you?

I integrate MTN MoMo, M-Pesa and Airtel Money into Laravel / PHP, Python / Django, Node.js / Next.js apps: payments, callbacks, payouts and go-live, with tests.