Maly Payments API
One integration to collect from and pay out to mobile money wallets. REST, JSON, asynchronous, webhook-driven.
What the API does#
The API moves money in two directions. A payment collects funds from a customer's mobile money wallet into your wallet with Maly. A payout sends funds from your wallet to a customer's wallet.
Around those two flows sit the supporting endpoints: the methods enabled on your account, your wallet balances, and a ledger of every entry that moved money.
Three things to know first#
These three behaviours shape every integration. Read them before you write code.
- Monetary values are minor units, as strings. Never a float. See Amounts and currencies.
- Every payment and payout needs an idempotency key. It makes retries safe. See Idempotency.
- Nothing completes synchronously. You get pending back, then a webhook. See Status lifecycle.
Your first payment#
Once you have your base URL and API key, this is the whole thing. Paste your base URL into the bar at the top of the page and every example on this site will use it.
curl -X POST "{BASE_URL}/payments" \
-H "X-Api-Key: <your-api-key>" \
-H "Content-Type: application/json" \
-d '{
"method": "mobile_money_mtn",
"country": "CG",
"amount": { "value": "11800", "currency": "XAF" },
"reference": "ORDER-123",
"idempotency_key": "a4f3c2d1-0000-0000-0000-000000000000",
"customer": { "phone": "242064661331" }
}' const res = await fetch("{BASE_URL}/payments", {
method: "POST",
headers: {
"X-Api-Key": process.env.MALY_API_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify({
method: "mobile_money_mtn",
country: "CG",
amount: { value: "11800", currency: "XAF" },
reference: "ORDER-123",
idempotency_key: crypto.randomUUID(),
customer: { phone: "242064661331" }
})
});
const payment = await res.json();
console.log(payment.id, payment.status);import os, uuid, requests
res = requests.post(
"{BASE_URL}/payments",
headers={
"X-Api-Key": os.environ["MALY_API_KEY"],
"Content-Type": "application/json",
},
json={
"method": "mobile_money_mtn",
"country": "CG",
"amount": {"value": "11800", "currency": "XAF"},
"reference": "ORDER-123",
"idempotency_key": str(uuid.uuid4()),
"customer": {"phone": "242064661331"},
},
)
payment = res.json()
print(payment["id"], payment["status"])<?php
$ch = curl_init("{BASE_URL}/payments");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("MALY_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"method" => "mobile_money_mtn",
"country" => "CG",
"amount" => ["value" => "11800", "currency" => "XAF"],
"reference" => "ORDER-123",
"idempotency_key" => bin2hex(random_bytes(16)),
"customer" => ["phone" => "242064661331"],
]),
]);
$payment = json_decode(curl_exec($ch), true);
echo $payment["id"], " ", $payment["status"];You get 202 Accepted with a payment in pending. The customer approves on their handset, and the outcome arrives at your webhook endpoint.
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"method": "mobile_money_mtn",
"country": "CG",
"amount": { "value": "11800", "currency": "XAF" },
"reference": "ORDER-123",
"idempotency_key": "a4f3c2d1-0000-0000-0000-000000000000",
"provider_reference": null,
"failure_reason": null,
"created_at": "2026-06-27T09:00:00Z",
"updated_at": "2026-06-27T09:00:00Z"
}Next: Work through the go-live checklist. It is the same list we certify against before enabling production.
Maly Tech Ltd. This guide is provided for information. Where it differs from your signed agreement, the agreement applies.