Skip to main content

Endpoints

Last updated: 2026-05-22

Canonical schema is merchants.swagger.json.

Request context dimensions used across endpoints

trafficVertical values:

  • TRAFFIC_VERTICAL_UNSPECIFIED
  • TRAFFIC_VERTICAL_GAMBLING
  • TRAFFIC_VERTICAL_FOREX
  • TRAFFIC_VERTICAL_OTHER

customerSegment values:

  • CUSTOMER_SEGMENT_UNSPECIFIED
  • CUSTOMER_SEGMENT_FTD
  • CUSTOMER_SEGMENT_TRUSTED
  • CUSTOMER_SEGMENT_VIP

isGambling remains available for legacy compatibility. For new integrations, prefer trafficVertical.

For country/currency/method discovery, start with the market catalog in this page. Treat it as the availability snapshot for your account, then use the bank and operator discovery endpoints only when the selected method needs additional detail.

Market catalog

GET /merchant/api/v1/catalog
Auth: Public key (pk_*)

Use this endpoint first to retrieve the countries, currencies, and method families enabled for your merchant account.

Response:

{
"markets": [
{
"countryCode": "GH",
"currencyCode": "GHS",
"methodsIn": ["mobile_money"],
"methodsOut": ["mobile_money"]
},
{
"countryCode": "NG",
"currencyCode": "NGN",
"methodsIn": ["bank_transfer"],
"methodsOut": ["bank_account"]
}
]
}

methodsIn and methodsOut use lower-case catalog tokens such as mobile_money, bank_transfer, and bank_account.

Treat these values as discovery tokens only. When you create orders, continue to send the request enum literals such as PAYMENT_METHOD_MOBILE_MONEY, PAYMENT_METHOD_BANK_TRANSFER, and PAYMENT_METHOD_BANK_ACCOUNT.

Use the selected market together with the request context for your flow when you call the discovery and order endpoints below.

List pay-in banks

GET /merchant/api/v1/payins/banks
Auth: Public key (pk_*)

NameTypeRequired (Swagger)Required (actual)Notes
countryCodestringNoYesISO 3166-1 alpha-2 (uppercased by the API)
paymentMethodstring (enum)NoYesUse PAYMENT_METHOD_BANK_ACCOUNT
trafficVerticalstring (enum)NoNoOptional traffic category
customerSegmentstring (enum)NoNoOptional customer segment

Example:

GET /merchant/api/v1/payins/banks?countryCode=NG&paymentMethod=PAYMENT_METHOD_BANK_ACCOUNT&trafficVertical=TRAFFIC_VERTICAL_OTHER&customerSegment=CUSTOMER_SEGMENT_FTD
Authorization: Bearer pk_test_...

Response:

{
"banks": [
{ "code": "033", "name": "United Bank for Africa" }
]
}

Use this endpoint after GET /merchant/api/v1/catalog when the selected inbound method needs bank detail.

Possible non-200 outcomes:

  • INVALID_ARGUMENT when paymentMethod or countryCode is missing
  • INTERNAL when bank discovery cannot be completed

List pay-in mobile money operators

GET /merchant/api/v1/payins/mmo
Auth: Public key (pk_*)

NameTypeRequired (Swagger)Required (actual)Notes
countryCodestringNoYesISO 3166-1 alpha-2
paymentMethodstring (enum)NoYesUse PAYMENT_METHOD_MOBILE_MONEY
trafficVerticalstring (enum)NoNoOptional traffic category
customerSegmentstring (enum)NoNoOptional customer segment

cURL example:

curl -G "https://merchants-api.example.com/merchant/api/v1/payins/mmo" \
-H "Authorization: Bearer pk_test_..." \
--data-urlencode "countryCode=GH" \
--data-urlencode "paymentMethod=PAYMENT_METHOD_MOBILE_MONEY" \
--data-urlencode "trafficVertical=TRAFFIC_VERTICAL_OTHER"

Response:

{
"operators": [
{ "operator": "MTN", "name": "MTN MOBILE MONEY" }
]
}

Treat operators[].operator as an opaque machine-readable value. Reuse it exactly as returned in mobileMoneyOperator when initializing a mobile-money pay-in, or in mobileMoneyDetails.operator when creating a mobile-money pay-in.

These results reflect the countryCode, paymentMethod, trafficVertical, and customerSegment you send for your merchant account.

Possible non-200 outcomes:

  • INVALID_ARGUMENT when paymentMethod or countryCode is missing
  • INVALID_ARGUMENT when pay-in mobile money is not supported for the selected merchant and country
  • INTERNAL when operator discovery cannot be completed

Initialize pay-in (H2P)

POST /merchant/api/v1/payins/initialize
Auth: Secret key (sk_*)

FieldTypeRequired (Swagger)Required (actual)Notes
amountstring (int64)NoYesMinor units, must be > 0
currencyCodestringNoYesISO 4217
countryCodestringNoConditionalISO 3166-1 alpha-2 payment-market country code. Required for mobile-money pay-ins and country-scoped currencies such as NGN, KES, GHS, ZAR, XAF, and XOF. Send the selected market country from /catalog
paymentMethodstring (enum)NoYesMust not be PAYMENT_METHOD_UNSPECIFIED
merchantReferencestringNoYesMerchant-side reference
redirectUrlstringNoYesMerchant return URL
isGamblingbooleanNoNoLegacy flag; prefer trafficVertical
trafficVerticalstring (enum)NoNoPreferred traffic category
customerSegmentstring (enum)NoNoIf unspecified, the API uses CUSTOMER_SEGMENT_FTD
mobileMoneyOperatorstringNoNoOptional operator code for operator-aware mobile money initialization. Retrieve it with GET /merchant/api/v1/payins/mmo and reuse the returned operator exactly as returned

cURL example (H2P initialize):

curl -X POST "https://merchants-api.example.com/merchant/api/v1/payins/initialize" \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"amount": "10000",
"currencyCode": "NGN",
"countryCode": "NG",
"paymentMethod": "PAYMENT_METHOD_BANK_ACCOUNT",
"merchantReference": "MR-1001",
"redirectUrl": "https://merchant.example/return",
"trafficVertical": "TRAFFIC_VERTICAL_OTHER",
"customerSegment": "CUSTOMER_SEGMENT_FTD"
}'

Response:

{
"paymentOrder": {
"id": "a6e26e49-febd-4921-9514-b32a0426da66",
"merchantReference": "MR-1001",
"paymentMethod": "PAYMENT_METHOD_BANK_ACCOUNT",
"type": "PAYMENT_ORDER_TYPE_PAYMENT_IN",
"status": "PAYMENT_ORDER_STATUS_CREATED",
"amount": "10000",
"currencyCode": "NGN",
"paymentUrl": "https://pay.example/a6e26e49-febd-4921-9514-b32a0426da66",
"redirectUrl": "https://merchant.example/return",
"trafficVertical": "TRAFFIC_VERTICAL_OTHER",
"customerSegment": "CUSTOMER_SEGMENT_FTD",
"createdAt": "2025-10-30T14:30:00Z",
"updatedAt": "2025-10-30T14:30:00Z"
},
"expiresAt": "2025-10-30T15:00:00Z"
}

expiresAt is the hosted payment TTL in UTC: 30 minutes after creation.

Possible non-200 outcomes:

  • INVALID_ARGUMENT for missing or invalid amount, currencyCode, countryCode, paymentMethod, merchantReference, or redirectUrl
  • INVALID_ARGUMENT when the selected pay-in method is disallowed, not enabled for the merchant/corridor, the currency is unsupported, account setup is incomplete for the resolved currency/method, or amount limits reject the request
  • INTERNAL for service configuration or upstream processing failures outside the validated merchant input surface

Create pay-in (H2H)

POST /merchant/api/v1/payins/create
Auth: Secret key (sk_*)

FieldTypeRequired (Swagger)Required (actual)Notes
amountstring (int64)NoYesMinor units, must be > 0
currencyCodestringNoYesISO 4217
countryCodestringNoConditionalISO 3166-1 alpha-2 payment-market country code. Required for mobile-money pay-ins and country-scoped currencies such as NGN, KES, GHS, ZAR, XAF, and XOF. Send the selected market country from /catalog
paymentMethodstring (enum)NoYesMust not be PAYMENT_METHOD_UNSPECIFIED
merchantReferencestringNoYesMerchant-side reference
redirectUrlstringNoYesMerchant return URL
isGamblingbooleanNoNoLegacy flag; prefer trafficVertical
trafficVerticalstring (enum)NoNoPreferred traffic category
customerSegmentstring (enum)NoNoIf unspecified, the API uses CUSTOMER_SEGMENT_FTD
bankAccountDetailsobjectNoConditionalRequired for PAYMENT_METHOD_BANK_ACCOUNT
bankTransferDetailsobjectNoConditionalRequired for PAYMENT_METHOD_BANK_TRANSFER
mobileMoneyDetailsobjectNoConditionalRequired for PAYMENT_METHOD_MOBILE_MONEY
testDetailsobjectNoNoOptional test payload for PAYMENT_METHOD_TEST

The create pay-in endpoint validates the core order fields and the method detail block for the selected paymentMethod.

bankAccountDetails

FieldTypeRequired (actual)Notes
emailstringNoOptional contact field
namestringYesAccount-holder/customer name
bankCodestringYesBank identifier for the selected market

bankTransferDetails

FieldTypeRequired (actual)Notes
emailstringNoOptional contact field

mobileMoneyDetails

FieldTypeRequired (actual)Notes
emailstringNoOptional contact field
mobileNumberstringYesCustomer wallet number / MSISDN in the format expected by the selected mobile-money network
operatorstringConditionalRequired when the selected mobile-money flow is operator-aware
voucherPinstringConditionalVoucher PIN for voucher-based mobile-money pay-in routes

operator is available for operator-aware mobile money pay-ins. Retrieve it with GET /merchant/api/v1/payins/mmo, treat it as an opaque discovery value rather than a fixed public enum, and reuse it exactly as returned. For voucher-based routes, include voucherPin together with the selected operator and wallet number. See Mobile money, Vouchers, and Mobile money operators for integration examples.

cURL example (H2H pay-in with mobile money):

curl -X POST "https://merchants-api.example.com/merchant/api/v1/payins/create" \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"amount": "8500",
"currencyCode": "KES",
"countryCode": "KE",
"paymentMethod": "PAYMENT_METHOD_MOBILE_MONEY",
"merchantReference": "MM-2001",
"redirectUrl": "https://merchant.example/return",
"trafficVertical": "TRAFFIC_VERTICAL_OTHER",
"customerSegment": "CUSTOMER_SEGMENT_TRUSTED",
"mobileMoneyDetails": {
"email": "customer@example.com",
"mobileNumber": "254700000000",
"operator": "<operator_from_discovery>"
}
}'

cURL example (voucher-based H2H mobile-money pay-in):

curl -X POST "https://merchants-api.example.com/merchant/api/v1/payins/create" \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"amount": "10000",
"currencyCode": "BWP",
"countryCode": "BW",
"paymentMethod": "PAYMENT_METHOD_MOBILE_MONEY",
"merchantReference": "VCH-1001",
"redirectUrl": "https://merchant.example/return",
"trafficVertical": "TRAFFIC_VERTICAL_OTHER",
"mobileMoneyDetails": {
"mobileNumber": "26772345684",
"operator": "BWVCH",
"voucherPin": "1234567890123456"
}
}'

Response details by method:

  • bankAccountDetails.redirectUrl: redirect URL when additional authorization is required.
  • bankTransferDetails.accountName, accountNumber, expiresAt: virtual account payment instructions.
  • mobileMoneyDetails.isOtpAuthRequired, displayMessage: customer instruction and OTP requirement.

Possible non-200 outcomes:

  • INVALID_ARGUMENT for missing or invalid amount, currencyCode, countryCode, paymentMethod, merchantReference, redirectUrl, or required method detail fields
  • INVALID_ARGUMENT when the selected pay-in method is disallowed, not enabled for the merchant/corridor, the currency is unsupported, account setup is incomplete for the resolved currency/method, or amount limits reject the request
  • INTERNAL for service configuration or upstream processing failures outside the validated merchant input surface

Authorize OTP (mobile money)

POST /merchant/api/v1/payins/authorize-otp
Auth: Secret key (sk_*)

Use this endpoint when mobileMoneyDetails.isOtpAuthRequired is true in the create pay-in response.

FieldTypeRequired (Swagger)Required (actual)
paymentOrderIdstring (UUID)NoYes
tokenstringNoYes

Response:

FieldTypeMeaning
isAuthorizedbooleanOTP authorization accepted
isStkPromptRequiredbooleanSTK prompt continuation required
displayMessagestringMessage to show to the customer

Possible non-200 outcomes:

  • INVALID_ARGUMENT for missing or malformed paymentOrderId, or missing token
  • NOT_FOUND when the payment order does not exist
  • INTERNAL when OTP authorization cannot be completed for the saved payment

List pay-out banks

GET /merchant/api/v1/payouts/banks
Auth: Public key (pk_*)

NameTypeRequired (Swagger)Required (actual)Notes
countryCodestringNoYesISO 3166-1 alpha-2
paymentMethodstring (enum)NoYesUse PAYMENT_METHOD_BANK_ACCOUNT
trafficVerticalstring (enum)NoNoOptional traffic category
customerSegmentstring (enum)NoNoAccepted for request compatibility

Use this endpoint after GET /merchant/api/v1/catalog when the selected outbound method needs bank detail.

Possible non-200 outcomes:

  • INVALID_ARGUMENT when paymentMethod or countryCode is missing
  • INTERNAL when bank discovery cannot be completed

List pay-out mobile money operators

GET /merchant/api/v1/payouts/mmo
Auth: Public key (pk_*)

NameTypeRequired (Swagger)Required (actual)Notes
countryCodestringNoYesISO 3166-1 alpha-2
paymentMethodstring (enum)NoYesUse PAYMENT_METHOD_MOBILE_MONEY
trafficVerticalstring (enum)NoNoOptional traffic category
customerSegmentstring (enum)NoNoAccepted for request compatibility

cURL example:

curl -G "https://merchants-api.example.com/merchant/api/v1/payouts/mmo" \
-H "Authorization: Bearer pk_test_..." \
--data-urlencode "countryCode=GH" \
--data-urlencode "paymentMethod=PAYMENT_METHOD_MOBILE_MONEY" \
--data-urlencode "trafficVertical=TRAFFIC_VERTICAL_OTHER"

Response:

{
"operators": [
{ "operator": "MTN", "name": "MTN MOBILE MONEY" }
]
}

Treat operators[].operator as an opaque machine-readable value. Reuse it exactly as returned in mobileMoneyDetails.operator when creating a mobile-money payout.

These results reflect the countryCode, paymentMethod, and trafficVertical you send for your merchant account. Include customerSegment when you also use it in the payout request.

Possible non-200 outcomes:

  • INVALID_ARGUMENT when paymentMethod or countryCode is missing
  • INTERNAL when operator discovery cannot be completed

Create pay-out

POST /merchant/api/v1/payouts/create
Auth: Secret key (sk_*)

FieldTypeRequired (Swagger)Required (actual)Notes
amountstring (int64)NoYesMinor units, must be > 0
currencyCodestringNoYesISO 4217
countryCodestringNoConditionalISO 3166-1 alpha-2 payment-market country code. Required for country-scoped currencies such as NGN, KES, GHS, ZAR, XAF, and XOF. Send the selected market country from /catalog; use the same country across discovery and payout creation
paymentMethodstring (enum)NoYesMust not be PAYMENT_METHOD_UNSPECIFIED
merchantReferencestringNoYesMerchant-side reference
isGamblingbooleanNoNoLegacy flag; prefer trafficVertical
trafficVerticalstring (enum)NoNoPreferred traffic category
customerSegmentstring (enum)NoNoAccepted. If unspecified, the API uses CUSTOMER_SEGMENT_FTD. It may appear back in merchant-visible payment objects
bankAccountDetailsobjectNoConditionalRequired for PAYMENT_METHOD_BANK_ACCOUNT
mobileMoneyDetailsobjectNoConditionalRequired for PAYMENT_METHOD_MOBILE_MONEY
testDetailsobjectNoNoOptional test payload for PAYMENT_METHOD_TEST

bankAccountDetails (for PAYMENT_METHOD_BANK_ACCOUNT)

FieldTypeRequired (actual)
emailstringRequired only when currencyCode = "INR"
bankCodestringYes
accountstringYes

mobileMoneyDetails (for PAYMENT_METHOD_MOBILE_MONEY)

FieldTypeRequired (actual)
emailstringRequired only when currencyCode = "INR"
operatorstringYes
mobileNumberstringYes
accountNamestringConditional

For mobile-money payouts, first call GET /merchant/api/v1/payouts/mmo for the country and request context you plan to use, then reuse the returned operator value exactly as returned. mobileNumber is the beneficiary wallet number / MSISDN in the format expected by the selected mobile-money network. Send accountName when the selected mobile-money payout path requires the beneficiary account name.

The API accepts customerSegment on payout requests. If you send it, the value may appear back in merchant-visible payment objects.

Balance check note:

  • POST /merchant/api/v1/payouts/create does not perform a pre-flight insufficient-balance validation.
  • If your flow requires pre-funding checks, read Balances before creating a payout.

Possible non-200 outcomes:

  • INVALID_ARGUMENT for missing or invalid amount, currencyCode, countryCode, paymentMethod, merchantReference, or required payout detail blocks/fields
  • INVALID_ARGUMENT when the selected payout method is disallowed for merchant payout flows
  • INTERNAL for service configuration or upstream processing failures

cURL example (bank-account payout):

curl -X POST "https://merchants-api.example.com/merchant/api/v1/payouts/create" \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"amount": "5000",
"currencyCode": "NGN",
"countryCode": "NG",
"paymentMethod": "PAYMENT_METHOD_BANK_ACCOUNT",
"merchantReference": "PO-3001",
"trafficVertical": "TRAFFIC_VERTICAL_OTHER",
"bankAccountDetails": {
"email": "beneficiary@example.com",
"bankCode": "033",
"account": "0123456789"
}
}'

cURL example (mobile-money payout after discovery):

curl -X POST "https://merchants-api.example.com/merchant/api/v1/payouts/create" \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"amount": "5000",
"currencyCode": "GHS",
"countryCode": "GH",
"paymentMethod": "PAYMENT_METHOD_MOBILE_MONEY",
"merchantReference": "PO-3002",
"trafficVertical": "TRAFFIC_VERTICAL_OTHER",
"mobileMoneyDetails": {
"operator": "MTN",
"mobileNumber": "233244123456"
}
}'

Use the operator value returned by GET /merchant/api/v1/payouts/mmo for the selected market and request context you send. The example above shows the request format.

Get payment order

GET /merchant/api/v1/payment/{id}
Auth: Secret key (sk_*)

NameTypeRequired
idstring (UUID)Yes

Use this endpoint to confirm payment state after redirects/webhooks and for reconciliation polling.

cURL example:

curl -X GET "https://merchants-api.example.com/merchant/api/v1/payment/a6e26e49-febd-4921-9514-b32a0426da66" \
-H "Authorization: Bearer sk_test_..."

Response:

{
"id": "a6e26e49-febd-4921-9514-b32a0426da66",
"merchantReference": "MR-1001",
"paymentMethod": "PAYMENT_METHOD_BANK_ACCOUNT",
"type": "PAYMENT_ORDER_TYPE_PAYMENT_IN",
"status": "PAYMENT_ORDER_STATUS_PENDING",
"amount": "10000",
"currencyCode": "NGN",
"redirectUrl": "https://merchant.example/return",
"trafficVertical": "TRAFFIC_VERTICAL_OTHER",
"customerSegment": "CUSTOMER_SEGMENT_FTD",
"createdAt": "2025-10-30T14:30:00Z",
"updatedAt": "2025-10-30T14:30:01Z"
}

Possible non-200 outcomes:

  • INVALID_ARGUMENT when the path id is not a valid UUID
  • NOT_FOUND when the payment does not exist or its linked currency row cannot be loaded

See Core objects for the complete merchant-visible PaymentOrder field list and enum notes.

Simulate payment status

POST /merchant/api/v1/payment/{id}/simulate
Auth: Secret key (sk_*)

This endpoint is available only in non-production environments. Use it to move a test payment through the normal status update, journal, event, and webhook flow.

NameTypeRequired
idstring (UUID path parameter)Yes
statusstringYes
providerErrorstringNo

Supported status values:

  • completed
  • expired
  • declined

providerError can be sent with negative simulations when you need a failure message in the simulated result.

cURL example:

curl -X POST "https://merchants-api.example.com/merchant/api/v1/payment/a6e26e49-febd-4921-9514-b32a0426da66/simulate" \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"status": "completed"
}'

Response: PaymentOrder.

Possible non-200 outcomes:

  • INVALID_ARGUMENT when id or status is invalid, or when the requested status transition is not allowed
  • NOT_FOUND when the payment does not exist
  • PERMISSION_DENIED in production or when the payment belongs to another merchant

Get balances

See Balances for GET /merchant/api/v1/balances, auth requirements, response shape, and payout pre-check guidance.