Skip to content

MTN MoMo Callback Validator

Paste the body MTN sent to your callback URL. You'll see whether the payment went through, what any failure reason means, and exactly what your code should do next. Nothing leaves your browser.

Callback body

Try a sample:
Compare with your order (optional, recommended)

If these don't match the callback, the tool tells you not to fulfil the order.

Result

What your handler should do

      Fields in this callback
      FieldValueMeaning

      Safe callback handler

      Accepts POST and PUT, replies fast, confirms with the status endpoint, and never pays out twice.

      How MoMo callbacks work

      When you send a Request to Pay with an X-Callback-Url header, MTN answers 202 Accepted straight away and pushes a prompt to the customer's phone. When the customer approves, declines or ignores it, MTN sends the result to your callback URL.

      The callback body has the same shape as the status response fromGET /collection/v1_0/requesttopay/{X-Reference-Id}: status, amount, currency,externalId, payer, and on success a financialTransactionId, or on failure a reason.

      Four rules for a safe handler

      1. Don't trust the body. Your callback URL is public. Treat the callback as "something changed", then confirm with the status endpoint before you release goods.
      2. Carry your own reference. Not every callback includes your X-Reference-Id. Add it to the URL yourself: /webhooks/momo?ref=<X-Reference-Id>.
      3. Accept POST and PUT, and reply fast. Return 200 immediately and do the work afterwards or in a queue.
      4. Be idempotent. The same callback can arrive more than once. If the order is already paid, do nothing.

      Also keep a fallback: if no final callback arrives within a few minutes, poll the status endpoint with backoff.

      MTN MoMo failure reason codes

      Failed payments include a reason, either as a plain string ("APPROVAL_REJECTED") or as an object ({"code": "...", "message": "..."}). Handle both. MTN's own documentation writes a few codes with a space (NOT ENOUGH_FUNDS), so normalise codes before you compare them.

      CodeMeaningRetry?What to do
      APPROVAL_REJECTEDThe customer declined the payment prompt on their phone.YesMark the order unpaid. Offer a "Try again" button; send a new Request to Pay with a new X-Reference-Id.
      EXPIREDThe customer did not approve the prompt before it timed out (about 5 minutes).YesMark the order unpaid and let the customer retry with a new X-Reference-Id.
      NOT_ENOUGH_FUNDSThe payer does not have enough money in their MoMo wallet, including fees.YesShow the total amount including any fees before you send the request, so customers can top up first.
      PAYER_LIMIT_REACHEDThe payer's transaction limit has been reached. Limits vary by country and account level.YesLet the customer pay with another number, or split the payment if your business allows it.
      PAYEE_LIMIT_REACHEDThe receiving account's limit has been reached. Limits vary by country.LaterCheck your merchant account limits with MTN, or reduce the amount.
      PAYER_NOT_FOUNDThe phone number is not a registered, active MoMo account.YesCheck the number format (country code, no leading 0). Use "Validate account holder status" before charging.
      PAYEE_NOT_FOUNDYour merchant (payee) account is not active — for debits this usually means the business account is blocked.NoContact your MTN account manager. Retrying will keep failing until your account is fixed.
      PAYEE_NOT_ALLOWED_TO_RECEIVEThe receiving wallet is not allowed to receive funds (for example, it is not active).NoCheck the receiving account status with MTN.
      COULD_NOT_PERFORM_TRANSACTIONThe transaction was not completed. Often the approval window passed or the network failed mid-way.YesRetry with a new X-Reference-Id. If it repeats, check the status endpoint and contact MTN.
      TRANSACTION_CANCELEDThe transaction was cancelled before completion.YesMark the order unpaid and allow a new attempt.
      PAYMENT_NOT_APPROVEDThe payment was not approved by the payer.YesMark the order unpaid and allow a new attempt.
      INTERNAL_PROCESSING_ERRORA generic error inside MTN's system.LaterCheck the status endpoint before retrying — the payment may still complete. Retry later with a new X-Reference-Id.
      SERVICE_UNAVAILABLEThe MoMo service is temporarily unavailable.LaterBack off and retry later. Consider showing another payment method meanwhile.
      NOT_ALLOWEDYour API user is not authorised for this operation.NoCheck your product subscription (Collections) and API user permissions with MTN.
      NOT_ALLOWED_TARGET_ENVIRONMENTYour credentials are not valid for the X-Target-Environment you sent (e.g. sandbox keys with mtnuganda).NoMake the target environment match your credentials. This is a configuration bug, not a customer problem.
      INVALID_CALLBACK_URL_HOSTThe X-Callback-Url host does not match the providerCallbackHost registered for your API user.NoUse a callback URL on the host you registered, or update the registration.
      INVALID_CURRENCYThe currency is not supported for this target environment (the sandbox only accepts EUR).NoSend EUR in the sandbox and the local currency in production (e.g. UGX for mtnuganda).
      RESOURCE_ALREADY_EXISTThe X-Reference-Id was already used. Every request needs a new UUID v4.YesGenerate a fresh UUID v4 for every Request to Pay. Never reuse one, even for a retry.
      RESOURCE_NOT_FOUNDMTN could not find the resource you referred to.YesCheck you are using the right X-Reference-Id and target environment.
      TRANSACTION_NOT_FOUNDThe transaction could not be found.YesCheck the reference ID and target environment you used for the status lookup.
      VALIDATION_ERRORThe request failed validation: wrong data types, unsupported decimals, empty fields, or notes over 128 characters.NoFix the request: amount as a string, supported currency, non-empty payerMessage/payeeNote of 128 characters or fewer.

      Codes from MTN's published failure-reasons list and the MoMo Open API ErrorReason enum, checked October 2026.

      For HTTP errors and full explanations with sources, see the MTN MoMo error code reference, or search every provider in the Payment Error Code Lookup.

      Frequently asked questions

      Can I mark an order as paid when the MoMo callback says SUCCESSFUL?

      Not on the callback alone. Anyone who knows your callback URL can send it a fake request. Use the callback as a signal, then call the Request to Pay status endpoint with your X-Reference-Id and only fulfil the order if that response says SUCCESSFUL and the amount and currency match.

      Does MTN send the MoMo callback as POST or PUT?

      Reports differ between versions and countries, so accept both methods on your callback route. The handler code generated by this tool does that.

      How do I match a MoMo callback to my order?

      Don't rely on the body containing your X-Reference-Id. Put it in the callback URL yourself, for example https://yourdomain.com/webhooks/momo?ref=<X-Reference-Id>, and also check that externalId matches the order ID you sent.

      Why am I not receiving MoMo callbacks?

      The most common causes are a callback URL on a different host from the providerCallbackHost registered for your API user, a URL that is not publicly reachable (localhost), a non-HTTPS URL in production, or a firewall or CSRF check rejecting the request. Callbacks can also be delayed, so always have a status-polling fallback.

      What does APPROVAL_REJECTED mean in MTN MoMo?

      The customer saw the payment prompt on their phone and declined it. Mark the order unpaid and let them try again with a new X-Reference-Id.

      Related

      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.