Skip to main content

Pay-ins API

Last updated: 2026-07-17

Pay-in operations use a secret key (sk_*). Monetary amounts are strings in minor units.

Initialize a hosted pay-in

POST /merchant/api/v1/payins/initialize

Auth: Secret key

Body fieldTypePresenceMeaning
amountstring (int64)RequiredPositive amount in minor units
currencyCodestringRequiredISO 4217 currency
countryCodestringConditionalSelected payment-market country; required for mobile money and whenever the selected currency/method maps to more than one enabled country
paymentSubMethodstringOptionalSupported only for bank transfer; current value is moniepoint
paymentMethodenumRequiredSelected PAYMENT_METHOD_* value
merchantReferencestringRequiredMerchant-side payment reference
redirectUrlstringRequiredCustomer return URL
trafficVerticalenumOptionalPreferred traffic category
customerSegmentenumOptionalCustomer segment; defaults when omitted
mobileMoneyOperatorstringConditionalPublic operator code for operator-required standard mobile money; required transfer rail code for mobile money transfer
{
"amount": "10000",
"currencyCode": "NGN",
"countryCode": "NG",
"paymentMethod": "PAYMENT_METHOD_BANK_ACCOUNT",
"merchantReference": "MR-1001",
"redirectUrl": "https://merchant.example/return",
"trafficVertical": "TRAFFIC_VERTICAL_OTHER"
}

The response contains paymentOrder and expiresAt. The hosted payment expires 30 minutes after creation; treat the returned UTC expiresAt value as the exact deadline for that order. Redirect the customer only when paymentOrder.paymentUrl is returned, and reconcile the payment after the customer returns.

Create a direct pay-in

POST /merchant/api/v1/payins/create

Auth: Secret key

The core fields are the same as hosted initialization, except that direct requests put method-specific input in exactly one matching detail block. Use paymentSubMethod only with bank transfer; the current supported value is moniepoint.

Detail blockUsed withFields
bankAccountDetailsPAYMENT_METHOD_BANK_ACCOUNTname and bankCode required; email optional
bankTransferDetailsPAYMENT_METHOD_BANK_TRANSFERemail optional
mobileMoneyDetailsPAYMENT_METHOD_MOBILE_MONEYmobileNumber required; operator and voucherPin conditional; email optional
mobileMoneyTransferDetailsPAYMENT_METHOD_MOBILE_MONEY_TRANSFERoperator and mobileNumber required
cardDetailsPAYMENT_METHOD_CARDSchema-visible only; card creation is not currently supported
testDetailsPAYMENT_METHOD_TESTNon-production controls described in Test method controls

Example direct mobile money request:

{
"amount": "8500",
"currencyCode": "KES",
"countryCode": "KE",
"paymentMethod": "PAYMENT_METHOD_MOBILE_MONEY",
"merchantReference": "MM-2001",
"redirectUrl": "https://merchant.example/return",
"mobileMoneyDetails": {
"mobileNumber": "<customer_msisdn>",
"operator": "<operator_from_discovery>"
}
}

Method-specific response details can include:

  • bankAccountDetails.redirectUrl for an authorization redirect;
  • bank transfer account instructions and expiresAt;
  • mobile money instructions, displayMessage, and isOtpAuthRequired.
  • mobile money transfer instructions, expiresAt, displayMessage, and providerReference.

See Mobile money transfer objects for its direct request, response, and instruction fields.

Authorize pay-in OTP

POST /merchant/api/v1/payins/authorize-otp

Auth: Secret key

Call this operation only when the create response sets mobileMoneyDetails.isOtpAuthRequired to true.

Body fieldTypePresence
paymentOrderIdstring (UUID)Required
tokenstringRequired

The response contains:

  • isAuthorized — whether OTP authorization was accepted;
  • isStkPromptRequired — whether the flow continues with an STK prompt;
  • displayMessage — customer-facing next-step text.

Outcomes

Pay-in operations can return INVALID_ARGUMENT for missing, malformed, or unavailable request values. A route that is temporarily unavailable can return the public method_unavailable reason. OTP authorization can also return NOT_FOUND for an unknown payment order. Handle all non-success responses using Errors.

Related guide: Pay-ins.