Endpoints
Last updated: 2026-05-22
Canonical schema is merchants.swagger.json.
Request context dimensions used across endpoints
trafficVertical values:
TRAFFIC_VERTICAL_UNSPECIFIEDTRAFFIC_VERTICAL_GAMBLINGTRAFFIC_VERTICAL_FOREXTRAFFIC_VERTICAL_OTHER
customerSegment values:
CUSTOMER_SEGMENT_UNSPECIFIEDCUSTOMER_SEGMENT_FTDCUSTOMER_SEGMENT_TRUSTEDCUSTOMER_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_*)
| Name | Type | Required (Swagger) | Required (actual) | Notes |
|---|---|---|---|---|
countryCode | string | No | Yes | ISO 3166-1 alpha-2 (uppercased by the API) |
paymentMethod | string (enum) | No | Yes | Use PAYMENT_METHOD_BANK_ACCOUNT |
trafficVertical | string (enum) | No | No | Optional traffic category |
customerSegment | string (enum) | No | No | Optional 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_ARGUMENTwhenpaymentMethodorcountryCodeis missingINTERNALwhen bank discovery cannot be completed
List pay-in mobile money operators
GET /merchant/api/v1/payins/mmo
Auth: Public key (pk_*)
| Name | Type | Required (Swagger) | Required (actual) | Notes |
|---|---|---|---|---|
countryCode | string | No | Yes | ISO 3166-1 alpha-2 |
paymentMethod | string (enum) | No | Yes | Use PAYMENT_METHOD_MOBILE_MONEY |
trafficVertical | string (enum) | No | No | Optional traffic category |
customerSegment | string (enum) | No | No | Optional 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_ARGUMENTwhenpaymentMethodorcountryCodeis missingINVALID_ARGUMENTwhen pay-in mobile money is not supported for the selected merchant and countryINTERNALwhen operator discovery cannot be completed
Initialize pay-in (H2P)
POST /merchant/api/v1/payins/initialize
Auth: Secret key (sk_*)
| Field | Type | Required (Swagger) | Required (actual) | Notes |
|---|---|---|---|---|
amount | string (int64) | No | Yes | Minor units, must be > 0 |
currencyCode | string | No | Yes | ISO 4217 |
countryCode | string | No | Conditional | ISO 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 |
paymentMethod | string (enum) | No | Yes | Must not be PAYMENT_METHOD_UNSPECIFIED |
merchantReference | string | No | Yes | Merchant-side reference |
redirectUrl | string | No | Yes | Merchant return URL |
isGambling | boolean | No | No | Legacy flag; prefer trafficVertical |
trafficVertical | string (enum) | No | No | Preferred traffic category |
customerSegment | string (enum) | No | No | If unspecified, the API uses CUSTOMER_SEGMENT_FTD |
mobileMoneyOperator | string | No | No | Optional 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_ARGUMENTfor missing or invalidamount,currencyCode,countryCode,paymentMethod,merchantReference, orredirectUrlINVALID_ARGUMENTwhen 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 requestINTERNALfor 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_*)
| Field | Type | Required (Swagger) | Required (actual) | Notes |
|---|---|---|---|---|
amount | string (int64) | No | Yes | Minor units, must be > 0 |
currencyCode | string | No | Yes | ISO 4217 |
countryCode | string | No | Conditional | ISO 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 |
paymentMethod | string (enum) | No | Yes | Must not be PAYMENT_METHOD_UNSPECIFIED |
merchantReference | string | No | Yes | Merchant-side reference |
redirectUrl | string | No | Yes | Merchant return URL |
isGambling | boolean | No | No | Legacy flag; prefer trafficVertical |
trafficVertical | string (enum) | No | No | Preferred traffic category |
customerSegment | string (enum) | No | No | If unspecified, the API uses CUSTOMER_SEGMENT_FTD |
bankAccountDetails | object | No | Conditional | Required for PAYMENT_METHOD_BANK_ACCOUNT |
bankTransferDetails | object | No | Conditional | Required for PAYMENT_METHOD_BANK_TRANSFER |
mobileMoneyDetails | object | No | Conditional | Required for PAYMENT_METHOD_MOBILE_MONEY |
testDetails | object | No | No | Optional 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
| Field | Type | Required (actual) | Notes |
|---|---|---|---|
email | string | No | Optional contact field |
name | string | Yes | Account-holder/customer name |
bankCode | string | Yes | Bank identifier for the selected market |
bankTransferDetails
| Field | Type | Required (actual) | Notes |
|---|---|---|---|
email | string | No | Optional contact field |
mobileMoneyDetails
| Field | Type | Required (actual) | Notes |
|---|---|---|---|
email | string | No | Optional contact field |
mobileNumber | string | Yes | Customer wallet number / MSISDN in the format expected by the selected mobile-money network |
operator | string | Conditional | Required when the selected mobile-money flow is operator-aware |
voucherPin | string | Conditional | Voucher 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_ARGUMENTfor missing or invalidamount,currencyCode,countryCode,paymentMethod,merchantReference,redirectUrl, or required method detail fieldsINVALID_ARGUMENTwhen 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 requestINTERNALfor 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.
| Field | Type | Required (Swagger) | Required (actual) |
|---|---|---|---|
paymentOrderId | string (UUID) | No | Yes |
token | string | No | Yes |
Response:
| Field | Type | Meaning |
|---|---|---|
isAuthorized | boolean | OTP authorization accepted |
isStkPromptRequired | boolean | STK prompt continuation required |
displayMessage | string | Message to show to the customer |
Possible non-200 outcomes:
INVALID_ARGUMENTfor missing or malformedpaymentOrderId, or missingtokenNOT_FOUNDwhen the payment order does not existINTERNALwhen OTP authorization cannot be completed for the saved payment
List pay-out banks
GET /merchant/api/v1/payouts/banks
Auth: Public key (pk_*)
| Name | Type | Required (Swagger) | Required (actual) | Notes |
|---|---|---|---|---|
countryCode | string | No | Yes | ISO 3166-1 alpha-2 |
paymentMethod | string (enum) | No | Yes | Use PAYMENT_METHOD_BANK_ACCOUNT |
trafficVertical | string (enum) | No | No | Optional traffic category |
customerSegment | string (enum) | No | No | Accepted 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_ARGUMENTwhenpaymentMethodorcountryCodeis missingINTERNALwhen bank discovery cannot be completed
List pay-out mobile money operators
GET /merchant/api/v1/payouts/mmo
Auth: Public key (pk_*)
| Name | Type | Required (Swagger) | Required (actual) | Notes |
|---|---|---|---|---|
countryCode | string | No | Yes | ISO 3166-1 alpha-2 |
paymentMethod | string (enum) | No | Yes | Use PAYMENT_METHOD_MOBILE_MONEY |
trafficVertical | string (enum) | No | No | Optional traffic category |
customerSegment | string (enum) | No | No | Accepted 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_ARGUMENTwhenpaymentMethodorcountryCodeis missingINTERNALwhen operator discovery cannot be completed
Create pay-out
POST /merchant/api/v1/payouts/create
Auth: Secret key (sk_*)
| Field | Type | Required (Swagger) | Required (actual) | Notes |
|---|---|---|---|---|
amount | string (int64) | No | Yes | Minor units, must be > 0 |
currencyCode | string | No | Yes | ISO 4217 |
countryCode | string | No | Conditional | ISO 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 |
paymentMethod | string (enum) | No | Yes | Must not be PAYMENT_METHOD_UNSPECIFIED |
merchantReference | string | No | Yes | Merchant-side reference |
isGambling | boolean | No | No | Legacy flag; prefer trafficVertical |
trafficVertical | string (enum) | No | No | Preferred traffic category |
customerSegment | string (enum) | No | No | Accepted. If unspecified, the API uses CUSTOMER_SEGMENT_FTD. It may appear back in merchant-visible payment objects |
bankAccountDetails | object | No | Conditional | Required for PAYMENT_METHOD_BANK_ACCOUNT |
mobileMoneyDetails | object | No | Conditional | Required for PAYMENT_METHOD_MOBILE_MONEY |
testDetails | object | No | No | Optional test payload for PAYMENT_METHOD_TEST |
bankAccountDetails (for PAYMENT_METHOD_BANK_ACCOUNT)
| Field | Type | Required (actual) |
|---|---|---|
email | string | Required only when currencyCode = "INR" |
bankCode | string | Yes |
account | string | Yes |
mobileMoneyDetails (for PAYMENT_METHOD_MOBILE_MONEY)
| Field | Type | Required (actual) |
|---|---|---|
email | string | Required only when currencyCode = "INR" |
operator | string | Yes |
mobileNumber | string | Yes |
accountName | string | Conditional |
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/createdoes 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_ARGUMENTfor missing or invalidamount,currencyCode,countryCode,paymentMethod,merchantReference, or required payout detail blocks/fieldsINVALID_ARGUMENTwhen the selected payout method is disallowed for merchant payout flowsINTERNALfor 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_*)
| Name | Type | Required |
|---|---|---|
id | string (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_ARGUMENTwhen the pathidis not a valid UUIDNOT_FOUNDwhen 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.
| Name | Type | Required |
|---|---|---|
id | string (UUID path parameter) | Yes |
status | string | Yes |
providerError | string | No |
Supported status values:
completedexpireddeclined
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_ARGUMENTwhenidorstatusis invalid, or when the requested status transition is not allowedNOT_FOUNDwhen the payment does not existPERMISSION_DENIEDin 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.