Main resources
Cross-border
Transfer funds between wallets in different countries and send cross-border payouts from any wallet.
Overview
KPay isolates funds by country: payments received in Cameroon feed the CMR wallet, those received in Senegal the SEN wallet, etc. — even if the currency is the same (e.g. XAF).
Cross-border operations let you:
- Transfer funds between wallets — move your balance from one country to another (e.g. CMR → GAB), with automatic conversion if currencies differ.
- Cross-country payout — send a Mobile Money withdrawal to country B by debiting the country A wallet, with automatic currency conversion if needed.
Sandbox & Production
kpay_test_…) and in production (key kpay_live_…). In sandbox, use test numbers for payouts and funds are virtual.Inter-wallet transfer
Moves funds from one country's wallet to another country's wallet, within the same application. If currencies differ (e.g. XAF → XOF), KPay applies the real-time exchange rate.
Endpoint
POST/api/wallets/transfer
JWT Authentication
Authorization: Bearer <token>.The environment is automatic: a token generated with a
kpay_test_... key will operate on sandboxwallets, a token generated with a kpay_live_... key will operate on production. wallets. You don't need to specify anything.Where to find the applicationId?
- Via the API — call GET /api/v1/payments/me with your API keys. The response contains
application.id. - From the dashboard — on the My Applicationspage, each card has a button to copy the ID.
Parameters
Request body
applicationIdstringrequisApplication ID (UUID). Retrieve it via GET /api/v1/payments/me or from the dashboard.
sourceCountrystringrequisSource wallet country to debit (ISO 3166-1 alpha-3, e.g. CMR, GAB, SEN). Currency is deduced automatically.
destinationCountrystringrequisDestination wallet country to credit (ISO 3166-1 alpha-3). Currency is deduced automatically.
amountnumberrequisAmount to transfer in the SOURCE country's currency (e.g. 10000 XAF). Min 100, max 10,000,000. If currencies differ, the credited amount is converted at the exchange rate.
descriptionstringDescription / reason for the transfer.
externalIdstringUnique identifier on your system's side. A second call with the same externalId returns 409 Conflict (idempotency).
Example — Same currency (XAF → XAF)
Transfer 50,000 XAF from the Cameroon wallet to the Gabon wallet (both in XAF — no conversion).
{
"applicationId": "e7c3b4f5-8d9e-4a1b-9c2d-3e4f5a6b7c8d",
"sourceCountry": "CMR",
"destinationCountry": "GAB",
"amount": 50000,
"description": "Fund transfer CMR to GAB",
"externalId": "TRF-2026-001"
}{
"id": "a1b2c3d4-...",
"sourceTransaction": {
"id": "a1b2c3d4-...",
"reference": "TRF-OUT-ABC123",
"type": "WALLET_TRANSFER_OUT",
"amount": 50000,
"currency": "XAF",
"country": "CMR"
},
"destinationTransaction": {
"id": "e5f6a7b8-...",
"reference": "TRF-IN-ABC123",
"type": "WALLET_TRANSFER_IN",
"amount": 50000,
"currency": "XAF",
"country": "GAB"
},
"exchangeRate": 1,
"description": "Fund transfer CMR to GAB",
"externalId": "TRF-2026-001",
"createdAt": "2026-06-23T10:00:00.000Z"
}Example — Cross-currency (XAF → XOF)
Transfer 100,000 XAF from the Cameroon wallet to the Senegal wallet (XOF). The credited amount is converted at the real-time exchange rate.
{
"applicationId": "e7c3b4f5-8d9e-4a1b-9c2d-3e4f5a6b7c8d",
"sourceCountry": "CMR",
"destinationCountry": "SEN",
"amount": 100000,
"description": "Supply Senegal wallet",
"externalId": "TRF-2026-002"
}{
"id": "c3d4e5f6-...",
"sourceTransaction": {
"id": "c3d4e5f6-...",
"reference": "TRF-OUT-DEF456",
"type": "WALLET_TRANSFER_OUT",
"amount": 100000,
"currency": "XAF",
"country": "CMR"
},
"destinationTransaction": {
"id": "g7h8i9j0-...",
"reference": "TRF-IN-DEF456",
"type": "WALLET_TRANSFER_IN",
"amount": 101530,
"currency": "XOF",
"country": "SEN"
},
"exchangeRate": 1.0153,
"description": "Supply Senegal wallet",
"externalId": "TRF-2026-002",
"createdAt": "2026-06-23T10:05:00.000Z"
}Exchange rate
exchangeRate field in the response lets you verify the applied rate. For same-currency transfers (e.g. XAF → XAF between CMR and GAB), the rate is always 1.Error codes
Possible errors
400Bad RequestSource country = destination, unsupported country, insufficient balance, or invalid data.
403ForbiddenThe application does not belong to the connected user.
404Not FoundNo wallet found for the source country (you have never received a payment in this country).
409ConflictA transfer with this externalId already exists (idempotency).
Cross-country payout
The cross-country payout allows you to send money (Mobile Money) to a beneficiary in country B, while debiting the wallet of country A. This is done via the sourceCountry parameter of the existing withdrawal endpoint.
Endpoint
POST/api/v1/payments/withdraw
Authentication
kpay_test_xxxxxxxxxxxxxxxxkey. In sandbox, use a test number for the beneficiary.sourceCountry parameter
The sourceCountry parameter is the only addition compared to a standard withdrawal. It indicates which wallet to debit, regardless of the beneficiary's country.
Additional parameter
sourceCountrystringISO 3166-1 alpha-3 country code of the wallet to debit (e.g. CMR). By default, the system debits the wallet of the provider's country. Use this field to debit a wallet from another country.
Automatic currency conversion
payoutCurrency, payoutAmount and exchangeRate.Example — Cross-country payout
Send 10,000 XAF to an Orange number in Gabon, debiting the Cameroon wallet (same currency XAF).
const res = await fetch("https://admin.kpay.site/api/v1/payments/withdraw", {
method: "POST",
headers: {
"X-API-Key": process.env.KPAY_API_KEY,
"X-Secret-Key": process.env.KPAY_SECRET_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
"amount": 10000,
"provider": "ORANGE_GAB",
"phoneNumber": "237653456789",
"sourceCountry": "CMR",
"externalId": "PAYOUT-CROSS-001",
"description": "Supplier payment Gabon"
}),
});
const data = await res.json();{
"id": "wdr_abc789",
"reference": "KPAY-WD-20260623-ABC789",
"status": "PENDING",
"amount": 10000,
"netAmount": 9500,
"feeAmount": 500,
"currency": "XAF",
"isTest": true,
"message": "Withdrawal request received. Processing via Mobile Money."
}Cross-currency payout (automatic conversion)
Debit a CMR (XAF) wallet to pay a beneficiary in Côte d'Ivoire (XOF). The conversion is automatic.
const res = await fetch("https://admin.kpay.site/api/v1/payments/withdraw", {
method: "POST",
headers: {
"X-API-Key": process.env.KPAY_API_KEY,
"X-Secret-Key": process.env.KPAY_SECRET_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
"amount": 50000,
"provider": "MTN_MOMO_CIV",
"phoneNumber": "237653456789",
"sourceCountry": "CMR",
"externalId": "PAYOUT-CROSS-002",
"description": "Supplier payment Côte d'Ivoire"
}),
});
const data = await res.json();{
"id": "wdr_cross123",
"reference": "KPAY-WD-20260626-CROSS123",
"status": "PENDING",
"amount": 50000,
"netAmount": 47500,
"feeAmount": 2500,
"currency": "XAF",
"payoutCurrency": "XOF",
"payoutAmount": 48211.75,
"exchangeRate": 1.0150,
"isTest": true,
"message": "Withdrawal request received. Processing via Mobile Money."
}Cross-currency fields
payoutCurrency, payoutAmount and exchangeRate only appear when the source wallet currency differs from the destination provider. For same-currency payouts, the response remains identical to a standard payout.Default behavior (without sourceCountry)
Without sourceCountry, the system debits the wallet of the provider's country. A withdrawal to MTN_MOMO_CMR debits the CMR wallet, a withdrawal to ORANGE_GAB debits the GAB wallet. This is the standard behavior.
Usage scenarios
Scenario 1 — Direct payout (same currency)
Your customers pay in Cameroon (XAF). You want to pay a supplier in Gabon (XAF). Same currency → direct payout with sourceCountry.
Wallet CMR (XAF: 100 000)
│
└──► POST /api/v1/payments/withdraw
{ provider: "ORANGE_GAB", sourceCountry: "CMR", amount: 10000 }
│
└──► Beneficiary receives 9 500 XAF on Orange GabonCross-currency payout (single call)
Debit an XAF wallet to directly pay a beneficiary in a country with a different currency. The conversion is automatic.
Wallet CMR (XAF: 100 000)
│
└──► POST /api/v1/payments/withdraw
{ provider: "MTN_MOMO_CIV", sourceCountry: "CMR", amount: 50000 }
│
├── Fees: 2 500 XAF (5%)
├── Net: 47 500 XAF
├── Rate XAF → XOF: 1.0150
└──► Beneficiary receives ≈ 48 212 XOF on MTN Côte d'IvoireScenario 3 — Consolidate funds
You receive payments in multiple countries and want to centralize all your funds on a single wallet (e.g. CMR) to simplify management and withdrawals.
POST /api/wallets/transfer { sourceCountry: "GAB", destinationCountry: "CMR", amount: 30000 }
POST /api/wallets/transfer { sourceCountry: "COG", destinationCountry: "CMR", amount: 15000 }
Wallet CMR ← consolidation of all funds XAFSupported countries and currencies
Each country has a primary currency. Transfers between countries with the same currency are instant (1:1 rate). Cross-currency transfers use the real-time exchange rate.
| Zone | Country | Code | Currency |
|---|---|---|---|
| CEMAC (XAF) | Cameroon | CMR | XAF |
| Gabon | GAB | XAF | |
| Congo-Brazzaville | COG | XAF | |
| Intra-CEMAC transfers: 1:1 rate | |||
| UEMOA (XOF) | Senegal | SEN | XOF |
| Côte d'Ivoire | CIV | XOF | |
| Benin | BEN | XOF | |
| Intra-UEMOA transfers: 1:1 rate | |||
| Others | Kenya | KEN | KES |
| DR Congo | COD | CDF | |
| Uganda | UGA | UGX | |
| Rwanda | RWA | RWF | |
Monetary zones
Wave operators temporarily suspended
Best practices
- Idempotency: always use an
externalIdfor your cross-country transfers and payouts. This prevents duplicates in case of network retry. - Check the balance: before a transfer, check the source wallet balance via
GET /api/wallets/overview(dashboard) orGET /api/v1/payments/balance(API). - Prefer the direct payout with
sourceCountrywhen the currency is the same. The inter-wallet transfer is only necessary for cross-currency operations. - Exchange rate: the rate is fixed at the time of transfer and returned in the response. Keep it for your accounting.