Paiements
Un paiement envoie de l’argent de votre solde vers un portefeuille mobile money ou un compte bancaire. Le montant et les frais sont réservés à la création, et rendus si le paiement échoue.
/v1/payoutsCréer un paiementGET/v1/payouts/{id}Consulter un paiementGET/v1/payoutsLister les paiementsL’objet paiement
Attributs
Identifiant unique (UUID).
true en production, false en mode test.
Toujours payout.
succeeded et failed sont définitifs. En cas d’échec, le montant et les frais reviennent sur votre solde.
pendingprocessingsucceededfailedMontant reçu par le bénéficiaire, en unités mineures.
Code devise ISO 4217.
Frais Monerail, débités de votre solde en plus de amount.
Le bénéficiaire. Les numéros de compte bancaire sont masqués.
Voir les attributs
mobile_moneybankNom du bénéficiaire.
Mobile money seulement : code opérateur.
Mobile money seulement : numéro, format international.
Banque seulement : numéro de compte masqué.
Code pays.
Votre description.
Votre propre référence, telle qu’envoyée à la création.
Paires clé-valeur jointes à la création.
Raison de l’échec, si status vaut failed ou expired.
Détail transmis par le partenaire de paiement, s’il existe.
Date de création, ISO 8601 (UTC).
Dernière modification, ISO 8601 (UTC).
{
"id": "7b05e2b6-6638-4088-a984-7155dfd6a036",
"object": "payout",
"livemode": false,
"status": "pending",
"amount": 150000,
"currency": "XAF",
"fee": 2250,
"recipient": {
"type": "mobile_money",
"name": "Kossi Mensah",
"operator": "MTN",
"phone": "237670000777",
"country": "CM"
},
"description": "Invoice 2026-118",
"client_reference": "inv_118",
"metadata": {},
"failure_code": null,
"failure_message": null,
"created_at": "2026-10-30T13:47:06.394Z",
"updated_at": "2026-10-30T13:47:06.394Z"
}Créer un paiement
Réserve amount + fee sur votre solde et envoie le paiement. Échoue avec 402 balance_insufficient si votre solde ne couvre pas les deux. Renvoie 201, ou 202 avec status: "processing" si le partenaire de paiement n’a pas encore accusé réception : ne recréez pas le paiement, son résultat arrive par webhook.
Rôles de la clé : Propriétaire, Finance ou Valideur
Corps
Montant en unités mineures de currency (francs entiers pour XAF, XOF et GNF ; centimes pour CDF, par multiples de 100). Entier seulement.
Code ISO 4217.
XAFXOFGNFCDFNGNKESGHSUGXTZSRWFLe bénéficiaire.
Voir les attributs
mobile_moneybankNom du bénéficiaire, 2 à 80 caractères.
Mobile money : numéro du bénéficiaire.
Mobile money : code opérateur.
Banque : un code issu de Lister les banques, exactement tel que renvoyé. Les noms et codes partiels sont refusés avec 400 bank_account_invalid.
Banque : numéro de compte.
ISO 3166-1 alpha-2. Par défaut, le pays principal de la devise (CM pour XAF, SN pour XOF, GN pour GNF, CD pour CDF). Les virements bancaires sont disponibles dans les pays indiqués avec virement dans Couverture.
500 caractères au plus.
Votre propre référence, 64 caractères au plus : lettres, chiffres et _ . : / -.
Jusqu’à 20 paires clé-valeur. Clés de 40 caractères au plus (A-Z a-z 0-9 _ . -), valeurs enregistrées en texte, 500 caractères au plus.
Retour
Renvoie le nouvel objet paiement avec 201, ou 202 quand le résultat n’est pas encore définitif.
Erreurs à prévoir
| HTTP | Code | Description |
|---|---|---|
402 | balance_ | Votre solde (ou solde cartes) ne couvre pas le montant et les frais. |
400 | bank_ | bank_code ne correspond pas exactement à un code de Lister les banques, ou le numéro de compte est trop court. |
400 | bank_ | Les virements bancaires ne sont pas disponibles dans ce pays : payez vers un portefeuille mobile money. |
400 | country_ | Ce pays n’est pas ouvert pour cette devise. Voir Couverture. |
400 | test_ | Mode test : utilisez un des numéros de test de ce pays. Voir Tests. |
402 | recipient_ | Le partenaire de paiement a refusé le bénéficiaire. |
403 | permission_ | Le créateur de la clé n’a pas le rôle requis pour cette opération. |
429 | rate_ | Une limite de débit est atteinte. Attendez Retry-After secondes. Voir Limites de débit. |
503 | provider_ | Le partenaire de paiement ne peut pas traiter ce paiement pour l’instant. Réessayez plus tard. |
curl https://api.monerail.com/v1/payouts \
-H "Authorization: Bearer $MONERAIL_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: a1f3c9e2-5b7d-4c21-9f80-3e6b2d1c7a45" \
-d '{
"amount": 150000,
"currency": "XAF",
"recipient": {
"type": "mobile_money",
"name": "Kossi Mensah",
"phone": "237670000777",
"operator": "MTN"
},
"description": "Invoice 2026-118",
"client_reference": "inv_118"
}'const res = await fetch('https://api.monerail.com/v1/payouts', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.MONERAIL_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'a1f3c9e2-5b7d-4c21-9f80-3e6b2d1c7a45',
},
body: JSON.stringify({
amount: 150000,
currency: 'XAF',
recipient: {
type: 'mobile_money',
name: 'Kossi Mensah',
phone: '237670000777',
operator: 'MTN',
},
description: 'Invoice 2026-118',
client_reference: 'inv_118',
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.monerail.com/v1/payouts",
headers={"Authorization": f"Bearer {os.environ['MONERAIL_KEY']}", "Idempotency-Key": "a1f3c9e2-5b7d-4c21-9f80-3e6b2d1c7a45"},
json={
"amount": 150000,
"currency": "XAF",
"recipient": {
"type": "mobile_money",
"name": "Kossi Mensah",
"phone": "237670000777",
"operator": "MTN",
},
"description": "Invoice 2026-118",
"client_reference": "inv_118",
},
timeout=30,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{data['error']['code']}: {data['error']['message']}")
print(data)<?php
$ch = curl_init('https://api.monerail.com/v1/payouts');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('MONERAIL_KEY'),
'Content-Type: application/json',
'Idempotency-Key: a1f3c9e2-5b7d-4c21-9f80-3e6b2d1c7a45',
],
CURLOPT_POSTFIELDS => json_encode([
'amount' => 150000,
'currency' => 'XAF',
'recipient' => [
'type' => 'mobile_money',
'name' => 'Kossi Mensah',
'phone' => '237670000777',
'operator' => 'MTN',
],
'description' => 'Invoice 2026-118',
'client_reference' => 'inv_118',
]),
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
throw new RuntimeException($data['error']['code'] . ': ' . $data['error']['message']);
}
print_r($data);package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, err := json.Marshal(map[string]any{
"amount": 150000,
"currency": "XAF",
"recipient": map[string]any{
"type": "mobile_money",
"name": "Kossi Mensah",
"phone": "237670000777",
"operator": "MTN",
},
"description": "Invoice 2026-118",
"client_reference": "inv_118",
})
if err != nil {
panic(err)
}
req, err := http.NewRequest(http.MethodPost, "https://api.monerail.com/v1/payouts", bytes.NewReader(payload))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("MONERAIL_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "a1f3c9e2-5b7d-4c21-9f80-3e6b2d1c7a45")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var data map[string]any
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
panic(err)
}
if res.StatusCode >= 400 {
panic(fmt.Sprint(data["error"]))
}
fmt.Println(data)
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
String body = """
{
"amount": 150000,
"currency": "XAF",
"recipient": {
"type": "mobile_money",
"name": "Kossi Mensah",
"phone": "237670000777",
"operator": "MTN"
},
"description": "Invoice 2026-118",
"client_reference": "inv_118"
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.monerail.com/v1/payouts"))
.header("Authorization", "Bearer " + System.getenv("MONERAIL_KEY"))
.header("Content-Type", "application/json")
.header("Idempotency-Key", "a1f3c9e2-5b7d-4c21-9f80-3e6b2d1c7a45")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
throw new RuntimeException(response.body());
}
System.out.println(response.body()); // parse with your JSON library (Jackson, Gson…)
}
}require "json"
require "net/http"
uri = URI("https://api.monerail.com/v1/payouts")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("MONERAIL_KEY")}"
req["Content-Type"] = "application/json"
req["Idempotency-Key"] = "a1f3c9e2-5b7d-4c21-9f80-3e6b2d1c7a45"
req.body = JSON.generate({
amount: 150000,
currency: "XAF",
recipient: {
type: "mobile_money",
name: "Kossi Mensah",
phone: "237670000777",
operator: "MTN",
},
description: "Invoice 2026-118",
client_reference: "inv_118",
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)
raise "#{data["error"]["code"]}: #{data["error"]["message"]}" unless res.is_a?(Net::HTTPSuccess)
puts datausing System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("MONERAIL_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.monerail.com/v1/payouts");
request.Headers.Add("Idempotency-Key", "a1f3c9e2-5b7d-4c21-9f80-3e6b2d1c7a45");
request.Content = JsonContent.Create(new
{
amount = 150000,
currency = "XAF",
recipient = new
{
type = "mobile_money",
name = "Kossi Mensah",
phone = "237670000777",
@operator = "MTN",
},
description = "Invoice 2026-118",
client_reference = "inv_118",
});
using var response = await http.SendAsync(request);
var data = await response.Content.ReadFromJsonAsync<JsonElement>();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(data.GetProperty("error").GetProperty("message").GetString());
}
Console.WriteLine(data);{
"id": "7b05e2b6-6638-4088-a984-7155dfd6a036",
"object": "payout",
"livemode": false,
"status": "pending",
"amount": 150000,
"currency": "XAF",
"fee": 2250,
"recipient": {
"type": "mobile_money",
"name": "Kossi Mensah",
"operator": "MTN",
"phone": "237670000777",
"country": "CM"
},
"description": "Invoice 2026-118",
"client_reference": "inv_118",
"metadata": {},
"failure_code": null,
"failure_message": null,
"created_at": "2026-10-30T13:47:06.394Z",
"updated_at": "2026-10-30T13:47:06.394Z"
}Consulter un paiement
Si le paiement est encore en route, Monerail interroge d’abord le partenaire de paiement.
Rôles de la clé : Tous les rôles
Paramètres de chemin
Identifiant du paiement.
Retour
Renvoie l’objet paiement.
curl https://api.monerail.com/v1/payouts/7b05e2b6-6638-4088-a984-7155dfd6a036 \
-H "Authorization: Bearer $MONERAIL_KEY"const res = await fetch('https://api.monerail.com/v1/payouts/7b05e2b6-6638-4088-a984-7155dfd6a036', {
headers: {
Authorization: `Bearer ${process.env.MONERAIL_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message}`);
console.log(data);import os
import requests
res = requests.get(
"https://api.monerail.com/v1/payouts/7b05e2b6-6638-4088-a984-7155dfd6a036",
headers={"Authorization": f"Bearer {os.environ['MONERAIL_KEY']}"},
timeout=30,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{data['error']['code']}: {data['error']['message']}")
print(data)<?php
$ch = curl_init('https://api.monerail.com/v1/payouts/7b05e2b6-6638-4088-a984-7155dfd6a036');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('MONERAIL_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
throw new RuntimeException($data['error']['code'] . ': ' . $data['error']['message']);
}
print_r($data);package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest(http.MethodGet, "https://api.monerail.com/v1/payouts/7b05e2b6-6638-4088-a984-7155dfd6a036", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("MONERAIL_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var data map[string]any
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
panic(err)
}
if res.StatusCode >= 400 {
panic(fmt.Sprint(data["error"]))
}
fmt.Println(data)
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.monerail.com/v1/payouts/7b05e2b6-6638-4088-a984-7155dfd6a036"))
.header("Authorization", "Bearer " + System.getenv("MONERAIL_KEY"))
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
throw new RuntimeException(response.body());
}
System.out.println(response.body()); // parse with your JSON library (Jackson, Gson…)
}
}require "json"
require "net/http"
uri = URI("https://api.monerail.com/v1/payouts/7b05e2b6-6638-4088-a984-7155dfd6a036")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("MONERAIL_KEY")}"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)
raise "#{data["error"]["code"]}: #{data["error"]["message"]}" unless res.is_a?(Net::HTTPSuccess)
puts datausing System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("MONERAIL_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Get, "https://api.monerail.com/v1/payouts/7b05e2b6-6638-4088-a984-7155dfd6a036");
using var response = await http.SendAsync(request);
var data = await response.Content.ReadFromJsonAsync<JsonElement>();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(data.GetProperty("error").GetProperty("message").GetString());
}
Console.WriteLine(data);{
"id": "7b05e2b6-6638-4088-a984-7155dfd6a036",
"object": "payout",
"livemode": false,
"status": "succeeded",
"amount": 150000,
"currency": "XAF",
"fee": 2250,
"recipient": {
"type": "mobile_money",
"name": "Kossi Mensah",
"operator": "MTN",
"phone": "237670000777",
"country": "CM"
},
"description": "Invoice 2026-118",
"client_reference": "inv_118",
"metadata": {},
"failure_code": null,
"failure_message": null,
"created_at": "2026-10-30T13:47:06.394Z",
"updated_at": "2026-10-30T13:47:09.807Z"
}Lister les paiements
Du plus récent au plus ancien.
Rôles de la clé : Tous les rôles
Paramètres de requête
20Nombre d’objets à renvoyer, de 1 à 100.
Curseur : l’identifiant du dernier objet de la page précédente.
Retour
Renvoie un objet liste : data contient jusqu’à limit objets payout, et has_more indique s’il en reste.
curl "https://api.monerail.com/v1/payouts?limit=10" \
-H "Authorization: Bearer $MONERAIL_KEY"const res = await fetch('https://api.monerail.com/v1/payouts?limit=10', {
headers: {
Authorization: `Bearer ${process.env.MONERAIL_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message}`);
console.log(data);import os
import requests
res = requests.get(
"https://api.monerail.com/v1/payouts",
headers={"Authorization": f"Bearer {os.environ['MONERAIL_KEY']}"},
params={"limit": 10},
timeout=30,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{data['error']['code']}: {data['error']['message']}")
print(data)<?php
$ch = curl_init('https://api.monerail.com/v1/payouts?limit=10');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('MONERAIL_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
throw new RuntimeException($data['error']['code'] . ': ' . $data['error']['message']);
}
print_r($data);package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest(http.MethodGet, "https://api.monerail.com/v1/payouts?limit=10", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("MONERAIL_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var data map[string]any
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
panic(err)
}
if res.StatusCode >= 400 {
panic(fmt.Sprint(data["error"]))
}
fmt.Println(data)
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.monerail.com/v1/payouts?limit=10"))
.header("Authorization", "Bearer " + System.getenv("MONERAIL_KEY"))
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
throw new RuntimeException(response.body());
}
System.out.println(response.body()); // parse with your JSON library (Jackson, Gson…)
}
}require "json"
require "net/http"
uri = URI("https://api.monerail.com/v1/payouts?limit=10")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("MONERAIL_KEY")}"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)
raise "#{data["error"]["code"]}: #{data["error"]["message"]}" unless res.is_a?(Net::HTTPSuccess)
puts datausing System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("MONERAIL_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Get, "https://api.monerail.com/v1/payouts?limit=10");
using var response = await http.SendAsync(request);
var data = await response.Content.ReadFromJsonAsync<JsonElement>();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(data.GetProperty("error").GetProperty("message").GetString());
}
Console.WriteLine(data);{
"object": "list",
"url": "/v1/payouts",
"data": [
{
"id": "7b05e2b6-6638-4088-a984-7155dfd6a036",
"object": "payout",
"livemode": false,
"status": "pending",
"amount": 150000,
"currency": "XAF",
"fee": 2250,
"recipient": {
"type": "bank",
"name": "Kossi Mensah",
"bank_code": "10005",
"account": "••••4821",
"country": "CM"
},
"description": "Invoice 2026-118",
"client_reference": "inv_118",
"metadata": {},
"failure_code": null,
"failure_message": null,
"created_at": "2026-10-30T13:47:06.394Z",
"updated_at": "2026-10-30T13:47:06.394Z"
}
],
"has_more": false
}