Skip to content
All providers

Notch Pay

payment·🇨🇲🇨🇮🇸🇳🇰🇪🇺🇬🇬🇭·Sandbox available

Notch Pay is a payment gateway enabling businesses to accept and send money via mobile money (MTN MoMo, Orange Money, M-Pesa, Wave, Airtel) across Cameroon, Côte d'Ivoire, Senegal, Kenya, Uganda and Ghana. Each market settles in its local currency (XAF in CM; XOF in CI/SN; KES, UGX, GHS).

Use with AI agents

After installing the plugin or adding the MCP server, prompt your agent:

“Block a Notch Pay customer to prevent them from initiating new payments.”
Install the plugin →

Capabilities

CapabilityTypeStatusiMethodExample
block_customersynchronousAvailablePOST
cancel_paymentsynchronousAvailableDELETE
create_beneficiarysynchronousAvailablePOST
create_customersynchronousAvailablePOST
create_paymentsynchronousAvailablePOST
create_webhooksynchronousAvailablePOST
delete_beneficiarysynchronousAvailableDELETE
delete_customersynchronousAvailableDELETE
delete_webhooksynchronousAvailableDELETE
get_balancesynchronousAvailableGET
get_beneficiarysynchronousAvailableGET
get_customersynchronousAvailableGET
get_webhooksynchronousAvailableGET
list_beneficiariessynchronousAvailableGET
list_channelssynchronousAvailableGET
list_countriessynchronousAvailableGET
list_currenciessynchronousAvailableGET
list_customer_payment_methodssynchronousAvailableGET
list_customer_paymentssynchronousAvailableGET
list_customerssynchronousAvailableGET
list_paymentssynchronousAvailableGET
list_transferssynchronousAvailableGET
list_webhookssynchronousAvailableGET
process_paymentsynchronousAvailablePOST
send_transfersynchronousAvailablePOST
unblock_customersynchronousAvailablePOST
update_customersynchronousAvailablePOST
update_webhooksynchronousAvailablePUT
verify_paymentsynchronousAvailableGET
verify_transfersynchronousAvailableGET
webhook_payment_completedwebhookAvailablePOST

Gotchas

block_customer

  • ⚠Blocking a customer is reversible — use unblock_customer to restore access. However, blocking does not cancel any payments already in progress at the time of the block.
  • ⚠The request body must be empty or omitted entirely. Sending a non-empty JSON body may cause unexpected behavior.
  • ⚠Calling block_customer on an already-blocked customer may return 200 without error. Do not rely on the response to determine whether the customer was previously unblocked.

cancel_payment

  • ⚠Only payments with status 'pending' can be canceled. Payments that are 'processing', 'complete', or 'failed' cannot be canceled and return a 422 error.
  • ⚠Cancellation is irreversible. Once canceled, the reference cannot be reused to restart the payment — create a new payment instead.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.

create_beneficiary

  • ⚠The Authorization header value is your public key with no prefix — do not add 'Bearer ' or 'Basic ' before it.
  • ⚠The channel slug must exactly match a Notch Pay supported channel (e.g. "cm.mtn", "cm.orange"). An invalid channel returns a 422 error without listing valid values.
  • ⚠account_number must be a valid phone number for mobile money channels — include the country code (e.g. +237XXXXXXXXX). Passing just a local number may fail silently or create an unusable beneficiary.
  • ⚠Store the returned beneficiary.id immediately — it is required to initiate transfers to this recipient and to delete the beneficiary later.
  • ⚠This endpoint requires your Notch Pay **private key** in the Authorization header, not the public key used for create_payment. Using a public key returns 406 Not Acceptable.
  • ⚠The channel slug determines the settlement currency: cm.* → XAF, ci.*/sn.* → XOF, ke.* → KES, ug.* → UGX, gh.* → GHS. The currency does not need to be specified at beneficiary creation but is fixed by the channel.

create_customer

  • ⚠At least one of email or phone is required. Submitting only name returns a 422 validation error — the Notch Pay docs make 'name' look mandatory but the API really enforces 'one of email/phone'.
  • ⚠The Authorization header must contain the public key directly as the token value — not prefixed with 'Bearer'. The format is exactly the key string.
  • ⚠Customer IDs returned by Notch Pay are opaque strings. Do not apply encodeURIComponent to these IDs when constructing path-based URLs — pass them as-is.
  • ⚠The address object uses 'address_line1' / 'address_line2' (and optional 'state'), not 'street'. Sending 'street' silently drops the field.

create_payment

  • ⚠Always verify payment status server-side by calling verify_payment (GET /payments/{reference}) before fulfilling an order. The callback redirect can be forged by the user.
  • ⚠The transaction object has no 'id' field — use 'reference' (format: trx.xxx) as the unique identifier. The 'sandbox' field is an integer (1 = sandbox, 0 = live), not a boolean.
  • ⚠Use a unique, idempotent reference per payment. Reusing a reference across different transactions will cause reconciliation errors.
  • ⚠The Authorization header value is your public key with no prefix — do not add 'Bearer ' before it.
  • ⚠For sandbox testing, use a public key containing 'test_' in its value. The same API endpoint is used for both test and live keys.
  • ⚠At least one of email, phone, or customer is required. Omitting all three returns a 422 error.
  • ⚠The currency must match the channel's country: XAF for cm.* channels (Cameroon), XOF for ci.*/sn.* (Côte d'Ivoire/Senegal), KES for ke.* (Kenya), UGX for ug.* (Uganda), GHS for gh.* (Ghana). A mismatch between currency and channel returns 422.

create_webhook

  • ⚠The webhook URL must be HTTPS. HTTP URLs are rejected with a 422 error.
  • ⚠Store the endpoint.id from the response — it is required to call update_webhook or delete_webhook later.
  • ⚠The events array must not be empty. Subscribing to no events is rejected. Only subscribe to event types your server actually handles.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.
  • ⚠This API registers the webhook configuration on the Notch Pay side. To validate incoming payloads, you must verify the X-Notch-Signature header on your receiving endpoint.
  • ⚠This endpoint requires your Notch Pay **private key** in the Authorization header, not the public key used for create_payment. Using a public key returns 406 Not Acceptable.

delete_beneficiary

  • ⚠The Authorization header value is your public key with no prefix — do not add 'Bearer ' before it.
  • ⚠Deletion is permanent and irreversible. There is no archive or soft-delete — once deleted, the beneficiary ID cannot be recovered.
  • ⚠A 404 response means either the beneficiary does not exist or it belongs to a different API key. Beneficiaries are scoped to the account that created them.
  • ⚠Do not URL-encode the beneficiary ID when inserting it into the path — pass it as-is.
  • ⚠This endpoint requires your Notch Pay **private key** in the Authorization header, not the public key used for create_payment. Using a public key returns 406 Not Acceptable.

delete_customer

  • ⚠Deletion is permanent and irreversible. There is no soft-delete or recycle bin — once deleted, the customer and all associated data cannot be recovered via the API.
  • ⚠A 404 is returned if the customer ID does not exist or no longer belongs to your account. Check for 404 separately from other error codes.
  • ⚠Deleting a customer does not automatically cancel or refund any associated payments. Resolve outstanding transactions before deleting the customer record.

delete_webhook

  • ⚠Deletion is immediate and irreversible. Notch Pay will stop delivering events to the endpoint right away.
  • ⚠The endpoint path parameter is the webhook endpoint ID, not its URL.
  • ⚠A 404 is returned if the endpoint ID does not exist or belongs to a different account.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.
  • ⚠This endpoint requires your Notch Pay **private key** in the Authorization header, not the public key used for create_payment. Using a public key returns 406 Not Acceptable.

get_balance

  • ⚠Use balance.available (not balance.total) to determine whether you have enough funds to initiate a transfer. total includes reserved and pending amounts that are not yet usable.
  • ⚠The response includes an environment field — verify it matches your expected mode ("live" vs "sandbox") to avoid acting on test data in production.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.
  • ⚠This endpoint requires your Notch Pay **private key** in the Authorization header, not the public key used for create_payment. Using a public key returns 406 Not Acceptable.

get_beneficiary

  • ⚠The Authorization header value is your public key with no prefix — do not add 'Bearer ' before it.
  • ⚠The id path parameter must be the Notch Pay beneficiary ID (from create_beneficiary response), not the account_number or phone number.
  • ⚠A 404 response means the beneficiary does not exist under the authenticated account — beneficiaries are scoped to the API key used to create them.
  • ⚠Do not URL-encode the beneficiary ID when inserting it into the path — pass it as-is.
  • ⚠This endpoint requires your Notch Pay **private key** in the Authorization header, not the public key used for create_payment. Using a public key returns 406 Not Acceptable.

get_customer

  • ⚠A 404 is returned when the customer ID does not exist or does not belong to your account. Always handle 404 explicitly rather than treating it as a generic error.
  • ⚠Customer IDs are opaque strings — pass them as-is into the URL path. Do not apply any encoding transformation to the ID.
  • ⚠The customer object does not include a 'blocked' or 'address' field — these are not returned by the API. Use block_customer / unblock_customer endpoints to manage block status.

get_webhook

  • ⚠The endpoint path parameter is the webhook endpoint ID, not its URL. Use the id field from create_webhook or list_webhooks.
  • ⚠A 404 is returned if the endpoint ID does not exist or belongs to a different account.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.
  • ⚠This endpoint requires your Notch Pay **private key** in the Authorization header, not the public key used for create_payment. Using a public key returns 406 Not Acceptable.

list_beneficiaries

  • ⚠The Authorization header value is your public key with no prefix — do not add 'Bearer ' before it.
  • ⚠Query parameters (limit, page, search) must be appended to the URL as a query string — they are not sent in the request body.
  • ⚠When current_page equals last_page, you have reached the end of the list. Requesting beyond last_page returns an empty items array rather than an error.
  • ⚠Pagination uses 'totals' (not 'total') and 'selected' (not 'per_page'). Reading the wrong field name silently returns undefined.
  • ⚠The totals field reflects the count at the time of the request; concurrent creates or deletes can cause inconsistencies when paginating large lists.
  • ⚠This endpoint requires your Notch Pay **private key** in the Authorization header, not the public key used for create_payment. Using a public key returns 406 Not Acceptable.

list_channels

  • ⚠Always filter by country and currency before displaying channels to users — not all channels are available in every country, and showing irrelevant options degrades UX.
  • ⚠Use min_amount and max_amount to validate the user's payment amount before initiating a transaction; submitting an out-of-range amount will result in a rejected payment.
  • ⚠Store the channel slug (not the id or name) when initiating a payment — it is the required identifier in subsequent API calls.
  • ⚠Cache the response — channel data changes rarely, but min/max amounts can be updated by Notch Pay without notice. A daily refresh is sufficient.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.

list_countries

  • ⚠This endpoint returns ALL 249 ISO 3166-1 countries — it is a reference/validation list, NOT a list of countries where Notch Pay operates. To find where Notch Pay actually has active channels, call GET /channels filtered by country instead.
  • ⚠Use the country code (not the country name) when filtering channels or initiating payments; the name is for display purposes only.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.

list_currencies

  • ⚠This endpoint returns ALL world currencies (ISO 4217 global list), NOT only currencies accepted by Notch Pay — it is a reference/validation endpoint. The actual currency used across all Notch Pay channels is XAF regardless of the customer's country.
  • ⚠Use the 'faction' field to format amounts correctly — XAF has faction: 0, so amounts must be integers. Sending 1000.50 for XAF will cause a validation error.
  • ⚠Use the currency code (not the name) when specifying currency in payment or channel API calls; the name field is for display purposes only.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.

list_customer_payment_methods

  • ⚠A 404 is returned if the customer ID does not exist. The customer ID must come from create_customer or list_customers — do not use a payment reference or beneficiary ID.
  • ⚠The items array may be empty if the customer has no saved payment methods — this is not an error. A customer only accumulates payment methods after completing at least one process_payment call.
  • ⚠The response schema for this endpoint was not live-tested (May 2026) — field names and structure are based on the OpenAPI spec and may differ from actual responses. Treat as draft.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.

list_customer_payments

  • ⚠A 404 is returned if the customer ID does not exist. Verify the customer exists via get_customer before listing their payments.
  • ⚠Pagination query parameters (limit, page) are passed as URL query string parameters. The customer ID is in the URL path, not the query string.
  • ⚠The payments array may be empty if the customer has no payments yet — this is not an error condition. Always check payments.length before iterating.
  • ⚠The top-level array is named 'payments' (not 'items') and pagination uses 'totals' (not 'total') and 'selected' (not 'per_page'). Reading the wrong field name silently returns undefined.

list_customers

  • ⚠Query parameters (limit, page, search) are passed as URL query string parameters, not in the request body. A GET request body is ignored.
  • ⚠Pagination is 1-based: the first page is page=1. Passing page=0 may return an empty result set or an error.
  • ⚠The 'totals' field (not 'total') returns the count of all matching customers. The 'selected' field (not 'per_page') indicates how many items were returned in this page. Using 'total' or 'per_page' will return undefined.
  • ⚠Pagination uses 'totals' (not 'total') and 'selected' (not 'per_page').

list_payments

  • ⚠All query parameters are optional — omitting all of them returns the full paginated list with the API default page size.
  • ⚠date_start and date_end must be ISO 8601 strings. Passing plain dates without a time component (e.g. "2024-01-01") may be rejected or interpreted differently by the API.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.
  • ⚠Iterate through pages using the page param and stop when current_page equals last_page to avoid requesting beyond the last page.
  • ⚠Pagination uses 'totals' (not 'total') and 'selected' (not 'per_page') — using the wrong field name will silently return undefined.

list_transfers

  • ⚠The Authorization header value is your public key with no prefix — do not add 'Bearer ' before it.
  • ⚠All transfer endpoints require an additional `X-Grant` header for enhanced security. Pass the grant token generated in your Notch Pay dashboard as `X-Grant: {token}`. Requests without this header return 401.
  • ⚠Pagination starts at page 1, not page 0. Passing page=0 may return an unexpected result or an error.
  • ⚠date_start and date_end must be ISO 8601 formatted strings. Passing plain date strings (e.g. '2024-01-01') without time and timezone may be rejected or silently ignored.
  • ⚠A transfer with status 'pending' is still in progress — do not treat it as completed. Only 'complete' status confirms a successful payout.
  • ⚠Pagination uses 'totals' (not 'total') and 'selected' (not 'per_page'). Reading the wrong field name silently returns undefined.
  • ⚠The totals field reflects the count across all pages, not just the current page. Use last_page to know when you have fetched all records.
  • ⚠This endpoint requires your Notch Pay **private key** in the Authorization header, not the public key used for create_payment. Using a public key returns 406 Not Acceptable.

list_webhooks

  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.
  • ⚠All query parameters are optional. Omitting them returns the full paginated list with the API default page size.
  • ⚠The top-level array is named 'endpoints' (not 'items'). Pagination uses 'totals' (not 'total') and 'selected' (not 'per_page'). Reading the wrong field name silently returns undefined.
  • ⚠Iterate through pages by incrementing the page param and stop when current_page equals last_page.
  • ⚠This endpoint requires your Notch Pay **private key** in the Authorization header, not the public key used for create_payment. Using a public key returns 406 Not Acceptable.

process_payment

  • ⚠process_payment must be called after create_payment — it requires the reference from the created transaction and will return 404 if the reference does not exist.
  • ⚠A 200 response with status "Accepted" means the payment was submitted to the mobile operator, not that it has been confirmed. Always use verify_payment afterwards to check the final status before fulfilling an order.
  • ⚠The phone number in data.phone must be in E.164 format (e.g. "+237670000000"). Passing a local format without the country code will cause the mobile operator to reject the request.
  • ⚠The channel slug is country-and-operator specific (e.g. "cm.mtn", "cm.orange"). Using a slug that does not match the customer's operator will result in a failed payment.
  • ⚠Calling process_payment on a transaction that is already 'complete', 'canceled', or 'failed' returns 422 with message 'Payment cannot be processed'.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.
  • ⚠The API also accepts PUT /payments/{reference} with the same body and response — this is a legacy alias. Prefer POST.

send_transfer

  • ⚠Transfers are not immediately settled — the initial status is "pending". Always call verify_transfer (GET /transfers/{reference}) to confirm completion before recording the disbursement as successful.
  • ⚠Either beneficiary (saved beneficiary ID) or the recipient+channel pair is required. Providing neither returns a 422 error.
  • ⚠All transfer endpoints require an additional `X-Grant` header for enhanced security. Generate this grant token in your Notch Pay dashboard and pass it as `X-Grant: {token}`. Requests without this header return 401.
  • ⚠Your server's IP address must be whitelisted in the Notch Pay dashboard before API transfers will be accepted. Requests from non-whitelisted IPs are rejected regardless of valid API credentials.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.
  • ⚠This endpoint requires your Notch Pay **private key** in the Authorization header, not the public key used for create_payment. Using a public key returns 406 Not Acceptable.
  • ⚠The currency must match the channel's country: XAF for cm.* channels (Cameroon), XOF for ci.*/sn.* (Côte d'Ivoire/Senegal), KES for ke.* (Kenya), UGX for ug.* (Uganda), GHS for gh.* (Ghana). A mismatch returns 422.

unblock_customer

  • ⚠Calling unblock_customer on a customer that is not currently blocked may return 200 without error. Use get_customer to check the blocked field before unblocking if idempotency matters.
  • ⚠The request body must be empty or omitted entirely. Sending a non-empty JSON body may cause unexpected behavior.
  • ⚠After unblocking, the customer can immediately initiate new payments. Ensure any fraud review or compliance checks are completed before calling unblock_customer.

update_customer

  • ⚠Notch Pay uses POST (not PATCH or PUT) for partial updates. Only the fields included in the body are updated; omitted fields retain their current values.
  • ⚠Sending an empty body or only the id field will return a 422 validation error — include at least one field to update in the request body.
  • ⚠Customer IDs are opaque strings — pass them as-is into the URL path without any encoding.
  • ⚠The address object uses 'address_line1' / 'address_line2' (and optional 'state'), not 'street'. Sending 'street' silently drops the field.

update_webhook

  • ⚠Updating the events field replaces the entire list — it is not a merge. To add a single event type, fetch the current list via get_webhook first, append the new event, then send the full updated array.
  • ⚠The endpoint path parameter is the webhook endpoint ID, not its URL.
  • ⚠At least one body field (url, events, or description) must be provided; sending an empty body may return a 422.
  • ⚠The new URL must be HTTPS. HTTP URLs are rejected.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.
  • ⚠This endpoint requires your Notch Pay **private key** in the Authorization header, not the public key used for create_payment. Using a public key returns 406 Not Acceptable.

verify_payment

  • ⚠Only fulfill an order when transaction.status is "complete" — "pending" means the user has not paid yet.
  • ⚠The reference must come from the transaction object returned by create_payment. Do not use values from the callback URL parameters, which can be tampered with.
  • ⚠Use your public key in the Authorization header with no 'Bearer ' prefix — same key as create_payment.

verify_transfer

  • ⚠Only record a disbursement as successful when transfer.status is "complete". "pending" means the mobile money network has not yet confirmed delivery.
  • ⚠All transfer endpoints require an additional `X-Grant` header for enhanced security. Pass the grant token generated in your Notch Pay dashboard as `X-Grant: {token}`. Requests without this header return 401.
  • ⚠The Authorization header uses your public key with no 'Bearer ' prefix.
  • ⚠This endpoint requires your Notch Pay **private key** in the Authorization header, not the public key used for create_payment. Using a public key returns 406 Not Acceptable.

webhook_payment_completed

  • ⚠Never fulfill an order based solely on receiving a webhook — always call verify_payment (GET /payments/{reference}) server-side to confirm the transaction status before acting.
  • ⚠Verify the signature using `crypto.timingSafeEqual` against the raw request body BEFORE parsing JSON. A simple string comparison (`===`) is vulnerable to timing attacks.
  • ⚠Webhook handlers must be idempotent — Notch Pay retries on non-2xx responses with increasing delays, which can cause duplicate event delivery. Store processed event IDs in your database and skip already-processed events.
  • ⚠The `payment.processing` status means the payment is in progress on the mobile money network — do not fulfill the order yet. Only act on `payment.complete`.
  • ⚠Register your webhook URL in the Notch Pay dashboard and select the events to subscribe to (e.g. payment.complete). The endpoint URL must be HTTPS.

Details

Category
payment
Sandbox
Yes

Spec contributors