Merchant API Documentation
Server-to-server API for card and mobile money payments
Base URL: http://localhost:3000
Authentication
Create API keys in the merchant portal at /m/api-keys. The plaintext key and secret are shown once at creation.
Include both headers on every request:
x-api-key: pk_...
x-api-secret: sk_...Try It – Card API
Sign in to your merchant account to use the interactive testing console.
Processing Modes
Card processing supports three modes: 2D, 3D, and AUTO.
- AUTO (recommended): Attempts 3D first, falls back to 2D if declined.
- 3D: Always requires 3D Secure authentication. Returns a
redirectUrl. - 2D: No redirect; status returned immediately.
Card Payments
Create Payment
POST /api/v1/payments
Creates a new card payment. For 3DS flows, returns a redirectUrl.
Required fields
amount,currencycustomer: firstName, lastName, emailbilling: firstName, lastName, street, city, country, postCodecard: holder, number, expMonth, expYear, securityCode
Optional fields
returnUrl— Where to redirect after 3DSreference— Your order/invoice referencedescription— Payment description
Example
curl -X POST http://localhost:3000/api/v1/payments \
-H 'Content-Type: application/json' \
-H 'x-api-key: pk_...' \
-H 'x-api-secret: sk_...' \
-d '{
"amount": 10.00,
"currency": "USD",
"reference": "order_123",
"returnUrl": "https://yoursite.com/payment/complete",
"customer": {
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com"
},
"billing": {
"firstName": "John",
"lastName": "Doe",
"street": "123 Main St",
"city": "New York",
"country": "US",
"postCode": "10001"
},
"card": {
"holder": "JOHN DOE",
"number": "4111111111111111",
"expMonth": "12",
"expYear": "30",
"securityCode": "123"
}
}'Response
{
"id": "tx_...",
"reference": "order_123",
"status": "INITIATED",
"redirectUrl": "https://gateway.example.com/3ds/...",
"returnUrl": "https://yoursite.com/payment/complete",
"createdAt": "2026-02-16T10:30:00.000Z"
}Force 3D Secure
POST /api/v1/payments/3d
Same request body as /api/v1/payments, but always forces 3DS authentication.
Mobile Money (APM) Payments
Accept mobile money payments across Africa. The API initiates a charge request and sends a USSD/STK push notification to the customer's phone for authorization.
Create APM Payment
POST /api/v1/apm/payments
Required fields
amount— Payment amount (number > 0)currency— ISO currency code (KES, GHS, TZS, etc.)customer.firstName,customer.lastNamecustomer.emailcustomer.phoneCode— Country calling code (e.g., 254)customer.phone— Phone number without country code
Optional fields
reference— Your order reference (auto-generated if omitted)description— Payment description
Example
curl -X POST http://localhost:3000/api/v1/apm/payments \
-H 'Content-Type: application/json' \
-H 'x-api-key: pk_...' \
-H 'x-api-secret: sk_...' \
-d '{
"amount": 1500,
"currency": "KES",
"reference": "order_123",
"description": "Mobile money payment",
"customer": {
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"phoneCode": "254",
"phone": "712345678"
}
}'Response
{
"id": "apm_1707425123456_abc123",
"pexiTransactionId": "PEXI1707425123456ABCD",
"reference": "order_123",
"status": "PENDING",
"createdAt": "2026-02-16T10:30:00.000Z",
"chargeRequestId": "6605",
"customer": {
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"phoneCode": "254",
"phone": "712345678"
}
}Supported Countries
Kenya (KES)
M-Pesa, Airtel Money
Ghana (GHS)
MTN MoMo, AirtelTigo, Vodafone Cash
Tanzania (TZS)
Vodacom M-Pesa, Airtel, Tigo
Rwanda (RWF)
MTN MoMo, Airtel Money
Zambia (ZMW)
MTN MoMo, Airtel Money
Senegal (XOF)
Orange Money, Free Money
Sierra Leone (SLE)
Orange Money, Africell Money
Integration Flow
- Call
POST /api/v1/apm/paymentswith customer details and amount - Customer receives USSD/STK push on their phone
- Customer authorizes payment by entering their PIN
- Webhook notification sent to your configured URL
- Update order status based on webhook (status 700 = success, 701 = failed)
Webhook Status Codes
700— Payment successful (CAPTURED)701— Payment failed (FAILED)702— Payment reversed (REFUNDED)703— Payment pending (PROCESSING)
Mobile Money Payouts
Disburse funds to mobile money accounts across 12 African countries. Perfect for salary payments, vendor payments, and marketplaces.
Create Payout
POST /api/v1/apm/payouts
Initiates a payout to a beneficiary's mobile money account. Funds are typically received within 1-5 minutes.
Required fields
amount— Payout amount (number > 0)currency— ISO currency code (KES, XOF, GHS, etc.)beneficiary.firstName,beneficiary.lastNamebeneficiary.phoneCode— Country calling code (e.g., 254)beneficiary.phone— Phone number without country code
Optional fields
beneficiary.email— Beneficiary email addressreference— Your payout reference (auto-generated if omitted)description— Payout description
Example Request
curl -X POST http://localhost:3000/api/v1/apm/payouts \
-H 'Content-Type: application/json' \
-H 'x-api-key: pk_...' \
-H 'x-api-secret: sk_...' \
-d '{
"amount": 1500,
"currency": "KES",
"reference": "salary_march_2026",
"description": "Monthly salary payment",
"beneficiary": {
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@example.com",
"phoneCode": "254",
"phone": "740334661"
}
}'Response
{
"id": "payout_1234567890_abc123",
"reference": "salary_march_2026",
"status": "PENDING",
"createdAt": "2026-03-06T15:00:00Z",
"payoutRequestId": "987654321",
"beneficiary": {
"firstName": "Jane",
"lastName": "Doe",
"email": "jane@example.com",
"phoneCode": "254",
"phone": "740334661"
}
}Supported Countries
🇰🇪 Kenya (KES)
M-Pesa, Airtel Money
🇸🇳 Senegal (XOF)
Orange Money, Free Money
🇸🇱 Sierra Leone (SLE)
Orange Money, Africell
🇬🇭 Ghana (GHS)
MTN, Airtel, Vodafone
🇹🇿 Tanzania (TZS)
Vodacom, Airtel, Tigo
🇷🇼 Rwanda (RWF)
MTN, Airtel
🇿🇲 Zambia (ZMW)
MTN, Airtel, Zamtel
🇲🇼 Malawi (MWK)
Airtel, TNM
🇨🇲 Cameroon (XAF)
MTN, Orange
🇧🇯 Benin (XOF)
MTN, Moov
🇨🇮 Côte d'Ivoire (XOF)
MTN, Orange, Wave, Moov
🇨🇩 DR Congo (CDF)
Vodacom, Airtel, Orange
Payout Flow
- Call
POST /api/v1/apm/payoutswith beneficiary details and amount - Payout is submitted to LIPAD for processing
- Beneficiary receives funds in their mobile money account (1-5 minutes)
- Webhook notification sent to your configured URL with final status
- Update payout records based on webhook (status 700 = success, 701 = failed)
Payout Statuses
PENDING— Payout submitted, awaiting processingCAPTURED— Payout completed successfully (status code 700)FAILED— Payout failed (status code 701)
Webhook Payload
When a payout completes or fails, you'll receive a webhook notification:
{
"txId": "payout_1234567890_abc123",
"reference": "salary_march_2026",
"status": "CAPTURED",
"gateway": {
"disbursement_request_id": "987654321",
"external_reference": "salary_march_2026",
"event": "successful_payout",
"payment_status": 700,
"amount": "1500",
"beneficiary_msisdn": "+254740334661",
"beneficiary_name": "Jane Doe",
"payment_method_code": "MPESA_KEN",
"transaction_id": "QAB123XYZ",
"payment_date": "2026-03-06T15:05:00Z"
}
}💡 Best Practices
- Always use unique references for each payout
- Validate phone numbers before submission (E.164 format)
- Use webhooks for final status confirmation
- Monitor your LIPAD account balance before sending payouts
- Store payout records for reconciliation and audit trails
⚠️ Important Notes
- Payouts cannot be canceled once submitted
- Maximum payout amount varies by provider (e.g., KES 150,000 for M-Pesa)
- Ensure MERCHANT_APM role is enabled for your merchant account
- Some countries require LIPAD service codes - contact support for setup
Transactions
List Card Transactions
GET /api/v1/transactions
Query params
limit— Default 20, max 100cursor— For paginationstatus— Filter by statusfrom,to— Date range (ISO format)
Get Card Transaction
GET /api/v1/transactions/:id
Add ?includeRaw=1 to include full gateway response.
List APM Transactions
GET /api/v1/apm/transactions
Returns mobile money transactions with same query params as card transactions.
Get APM Transaction
GET /api/v1/apm/transactions/:id
Webhooks
PexiPay sends webhooks to notify your server about payment status changes in real-time.
Why Use Webhooks?
- Customers may close the browser before returning to your site
- Network issues can interrupt return URL redirects
- Enable automated order fulfillment and real-time updates
Configuration
Configure your webhook URL in the merchant dashboard at /m/settings.
Card Payment Webhook Payload
{
"txId": "tx_abc123...",
"status": "SUCCEEDED",
"amount": 10.00,
"currency": "USD",
"reference": "order_123",
"merchantId": "cm_...",
"timestamp": "2026-02-16T10:30:00.000Z",
"cardBrand": "VISA",
"cardLast4": "1111",
"customer": {
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com"
}
}APM Payment Webhook Payload
{
"event": "successful_payment",
"amount": "1500",
"currency_code": "KES",
"payer_msisdn": "254712345678",
"payer_email": "john@example.com",
"payment_status": 700,
"charge_request_id": "123456789",
"external_reference": "order_123",
"payment_method_code": "MPESA_KEN",
"transaction_id": "2019",
"payer_transaction_id": "RBL35TKMUH",
"payment_date": "2026-02-16T10:30:00.000Z"
}Card Payment Statuses
- SUCCEEDED — Payment completed
- DECLINED — Declined by issuing bank
- FAILED — Processing error
- CANCELLED — Customer cancelled
- PENDING — Awaiting final status
Implementation Example
app.post('/webhooks/pexipay', async (req, res) => {
// Acknowledge receipt immediately
res.status(200).json({ received: true });
const { txId, status, reference } = req.body;
switch (status) {
case 'SUCCEEDED':
await fulfillOrder(reference);
break;
case 'DECLINED':
case 'FAILED':
await notifyPaymentFailed(reference);
break;
}
});Timeouts & Retries
PexiPay expects a 200 response within 5 seconds. Failed webhooks are retried with exponential backoff.
Hosted Payment Links
Create payment links in the dashboard and accept card payments via a hosted flow. No API key is required for paying a link.
Pay Link
POST /api/payment-links/:id/pay
Required fields
returnUrl— Redirect URL after 3DScustomer— firstName, lastName, emailbilling— Full billing addresscard— Card details
Error Codes
400— Invalid request parameters401— Missing or invalid API credentials403— Merchant not authorized for this payment type404— Resource not found502— Payment gateway error503— Service temporarily unavailable
Postman Collection
Download and import for quick testing.
Set environment variables apiKey and apiSecret from your merchant portal.