Skip to content

Create a payment (pay-in or pay-out)

POST
/api/global/payments
curl -X POST "https://api.payhero.africa/api/global/payments" \
-u "API_USERNAME:API_PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"request_type": "payment",
"transaction_channel": "momo",
"provider": "yc",
"amount": 5500,
"currency": "KES",
"country": "KE",
"reason": "Order payment for invoice INV-2025-001",
"customer": {
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"phone": "+254712345678",
"dob": "1990-01-01T00:00:00Z",
"country": "KE",
"address": {
"line_1": "Westlands Road",
"country": "KE"
}
},
"vendor_config": {
"vendor_id": 63
},
"provider_config": {
"network_id": "7ea6df5c-6bba-46b2-a7e6-f511959e7edb",
"provider_id": "c2b2eeda-d4ca-49fd-ba21-0781ffa7714b",
"network_name": "M PESA",
"network_code": "7ea6df5c-6bba-46b2-a7e6-f511959e7edb",
"account_type": "momo"
},
"payment_config": {
"reference": "test_top_",
"account_number": "+254712345678",
"remark": "order payment",
"payment_category": "bill payment",
"callback_url": "https://payhero-dev.beeceptor.com",
"redirect_url": "https://payhero-dev.beeceptor.com"
}
}'

Initiates a collection (request_type: payment) or a disbursement (request_type: withdrawal). Works across all supported countries — set currency, country, provider, and the relevant provider_config.

Who is the customer? It is the owner of the funds being moved, not you as the platform. For a collection it is the client making the deposit; for a withdrawal it is the owner of the wallet being debited — normally the initiating merchant — while the destination is set in payment_config.account_number.

Reusing a counterparty. Rather than re-sending the customer details on every request, create a beneficiary once and send only customer.customer_uuid. The stored record supplies the name, email, phone, country, IDs, date of birth, address and destination account number; anything you send alongside it overrides the stored value for that transaction. Referencing a beneficiary also subjects the payment to that record’s KYC standing — see the error table below.

For global payments provider_config and payment_config are required in full. vendor_config.channel_id is not required: it only applies to Kenya-based collections that settle to an external channel (bank, paybill, till). source is assigned by the server — do not send it.

Take provider and provider_config from discover-rails: send the network’s channel_type as provider_config.account_type, pick the network whose ramp_type matches the direction (deposit to collect, withdraw to pay out), and include every field required_fields[provider] lists. Card payments require payment_config.redirect_url, and their response carries a checkout_url to send the payer to.

Beneficiary-related failures

Condition Status Message
UUID not found on your account 404 beneficiary "<uuid>" does not exist
Beneficiary deactivated 400 beneficiary is not active
Tier-0 KYC envelope exceeded 400 full KYC information is required for the transaction
No customer_uuid and inline fields missing 400 invalid_arguments[] naming each missing field
Media typeapplication/json
object
request_type
required

payment for pay-in (collection), withdrawal for pay-out (disbursement).

string
Allowed values: payment withdrawal
transaction_channel
required

Rail to use.

string
Allowed values: momo bank card
provider
required

Payment provider (see Discovery API).

string
amount
required
number
currency
required

ISO 4217 currency code.

string
country
required

ISO 3166-1 alpha-2 country code.

string
reason
string
source

Assigned by the server (api on the API-key surface). Ignored if you send it.

string
customer
required

The owner (custodian) of the funds being moved — not you as the platform.

  • Collection (request_type: payment) — the client making the deposit, i.e. the payer being charged.
  • Withdrawal (request_type: withdrawal) — the owner of the wallet being debited, which is normally the initiating merchant, not the person receiving the money. The destination is identified by payment_config.account_number.

Fill it one of two ways:

  1. By reference — send only customer_uuid, the uuid of a beneficiary you created earlier. The stored record supplies the name, email, phone, country, IDs, date of birth, address and destination account number. Any field you do send alongside it overrides the stored value for that one transaction.
  2. Inline — send the details on every request. first_name, last_name, email and phone are then all required, and nothing is saved to your beneficiary book.
object
customer_uuid

UUID of a stored beneficiary. When present, every other field here is optional and defaults to the stored record.

string format: uuid
customer_uid

Legacy alias for customer_uuid. Prefer customer_uuid in new integrations.

string format: uuid
first_name

Required unless customer_uuid is supplied.

string
last_name

Required unless customer_uuid is supplied.

string
email

Required unless customer_uuid is supplied.

string format: email
phone

Required unless customer_uuid is supplied. Usually the same as the account number.

string
dob

Date of birth. Required for bank rails; for mobile top-ups use your KYC’s DOB. Not required for mobile top-ups.

string format: date-time
country
string
id_type

Identity document type (e.g. national_id, NIN, passport).

string
id_number
string
additional_id_type
string
additional_id_number
string
address
object
line_1
string
country

ISO 3166-1 alpha-2 country code.

string
vendor_config
object
vendor_id

Your vendor ID.

integer
channel_id

Optional — not required for global payments. This references a payment channel ID and is only used for Kenya-based collections that settle to an external channel such as a bank account, paybill or till. Omit it for everything else.

integer
provider_config
required

Provider/network routing details, taken from the Discovery API (POST /api/global/transaction-providers). All fields are required for global payments, except channel_id.

object
network_id
required

Network ID returned by the Discovery API.

string
provider_id
required

Provider ID returned by the Discovery API.

string
network_name
required

Human-readable network name.

string
network_code
required

Network code returned by the Discovery API.

string
account_type
required

Destination account type, e.g. momo or bank.

string
channel_id

Optional. Provider channel ID; you can safely ignore this field.

string
payment_config
required

Payment metadata. All fields are required for global payments.

object
reference
required

Your own reference for the transaction.

string
account_number
required

The account being charged (collection) or paid (withdrawal). Backfilled from the beneficiary’s stored account_number when you pass customer.customer_uuid.

string
remark
required

Free-text remark shown on the transaction.

string
payment_category
required

Category of the payment.

string
callback_url
required

URL that receives the asynchronous transaction result.

string format: uri
redirect_url
required

URL the payer is returned to after completing the payment.

string format: uri
payment_split
object
amount_to_save
number
Examples

Pay-in using a stored beneficiary — `customer_uuid` replaces the customer details

{
"request_type": "payment",
"transaction_channel": "momo",
"provider": "viewtech",
"amount": 1500,
"currency": "KES",
"country": "KE",
"reason": "Order payment for invoice INV-2025-001",
"customer": {
"customer_uuid": "a2e0434e-17ae-41b1-a1cf-1694ad63ee99"
},
"vendor_config": {
"vendor_id": 63
},
"provider_config": {
"network_id": "7ea6df5c-6bba-46b2-a7e6-f511959e7edb",
"provider_id": "c2b2eeda-d4ca-49fd-ba21-0781ffa7714b",
"network_name": "M PESA",
"network_code": "7ea6df5c-6bba-46b2-a7e6-f511959e7edb",
"account_type": "momo"
},
"payment_config": {
"reference": "order_INV-2025-001",
"account_number": "+254712345678",
"remark": "order payment",
"payment_category": "bill payment",
"callback_url": "https://payhero-dev.beeceptor.com",
"redirect_url": "https://payhero-dev.beeceptor.com"
}
}

Payment request accepted and queued for processing.

Media typeapplication/json
object
status_code
string
merchant_reference
string
transaction_type
string
Allowed values: payin payout
success
boolean
message
string
checkout_request_id
string
gateway
string
conversation_id
string
provider_response

Raw provider response details.

object
key
additional properties
any
Examples

Pay-in accepted

{
"status_code": "200",
"merchant_reference": "9FD194041588.iI",
"transaction_type": "payin",
"success": true,
"message": "request sent",
"checkout_request_id": "a8e1c979-3592-5abd-b1cd-dc1dbd34e708",
"gateway": "yellowcard",
"conversation_id": "9FD194041588.iI",
"provider_response": {
"amount": 42.11,
"convertedAmount": 5500,
"country": "KE",
"currency": "KES",
"rate": 130.61,
"status": "process"
}
}

The request was rejected (validation or business-rule failure).

Media typeapplication/json
object
error_code
string
error_message
string
status_code
integer
Examples
{
"error_code": "invalid_argument",
"error_message": "the transaction amount is insufficient as it wont cater for cost: (4.62)",
"status_code": 400
}

Missing or invalid Basic auth credentials.

Media typeapplication/json
object
error_code
string
error_message
string
status_code
integer
Example
{
"error_code": "invalid_argument",
"error_message": "the transaction amount is insufficient as it wont cater for cost: (4.62)",
"status_code": 400
}

No beneficiary with that UUID exists on the account behind your API key. A UUID that belongs to another merchant also returns 404 — never 403.

Media typeapplication/json
object
error_code
string
error_message
string
status_code
integer
Examples
ExampleUnknown uuid
{
"error_code": "not_found",
"error_message": "beneficiary \"a2e0434e-17ae-41b1-a1cf-1694ad63ee99\" does not exist",
"status_code": 404
}