Collections
A collection asks a customer to pay from their mobile money wallet. The customer approves on their phone, or enters a one-time code that you send with authorize. Follow the outcome with webhooks.
/v1/collectionsCreate a collectionGET/v1/collections/{id}Retrieve a collectionGET/v1/collectionsList collectionsPOST/v1/collections/{id}/authorizeAuthorize with a codePOST/v1/collections/{id}/resend_otpResend the codeThe collection object
Attributes
Unique identifier (UUID).
true for live objects, false for test objects.
Always collection.
Where the collection stands. succeeded, failed and expired are final.
pendingrequires_otpprocessingsucceededfailedexpiredAmount requested from the customer, in minor units.
ISO 4217 currency code.
Monerail fee, set once the collection succeeds.
Amount credited to your balance (amount − fee), set once the collection succeeds.
The paying customer.
Show child attributes
Customer name.
Mobile money number, international format without +.
ISO 3166-1 alpha-2 country code.
Mobile money operator code, for example MTN or ORANGE.
Your description.
Your own reference, as sent at creation.
Key-value pairs you attached at creation.
What the customer must do next, or null. type is customer_approval (approve on the phone; ussd_code is a fallback), otp (send the code with authorize; when ussd_code is present, the customer dials it to get the code) or redirect (send the customer to url to approve).
Show child attributes
Kind of action.
customer_approvalotpredirectInstruction from the operator, to show to your customer.
USSD code the customer dials: fallback for customer_approval, code generation for otp.
redirect only: payment page to open for the customer.
failed or expired, when the collection did not succeed. The payment partner gives no finer reason for collections.
failedexpiredHuman-readable reason, when one is available.
Creation time, ISO 8601 (UTC).
Last change, ISO 8601 (UTC).
{
"id": "3b8f6c1e-2a47-4d1b-9e35-7c0a9d2f4e61",
"object": "collection",
"livemode": false,
"status": "pending",
"amount": 5000,
"currency": "XAF",
"fee": null,
"net": null,
"customer": {
"name": "Awa Ndiaye",
"phone": "237670000999",
"country": "CM"
},
"operator": "MTN",
"description": "Order 42",
"client_reference": "order_42",
"metadata": {
"order_id": "42"
},
"next_action": {
"type": "customer_approval",
"message": "A payment request was sent. The customer enters their PIN.",
"ussd_code": "*126#"
},
"failure_code": null,
"failure_message": null,
"created_at": "2026-10-30T13:47:06.394Z",
"updated_at": "2026-10-30T13:47:06.394Z"
}Create a collection
Sends a payment request to the customer’s phone. Returns 201 with the collection, or 202 if the operator has not confirmed receipt yet (the collection is then processing; its outcome arrives by webhook).
Key roles: Owner, Finance or Developer
Body
Amount in minor units of currency (whole francs for XAF, XOF and GNF; centimes for CDF, in multiples of 100). Integer only.
ISO 4217 code. Collections in NGN are not available and return 400 currency_unsupported.
XAFXOFGNFCDFKESGHSUGXTZSRWFCustomer mobile money number. International format (237670000000 or +237 670 00 00 00), or local format if country is known.
The paying customer.
Show child attributes
Customer name, 2 to 80 characters.
ISO 3166-1 alpha-2 code. Required to collect in a country other than the main country of the currency: XAF defaults to CM (also GA, TD, CG, CF), XOF to SN (also CI, BJ, TG, BF, ML, NE), GNF to GN, CDF to CD. See Coverage.
Up to 500 characters.
Your own reference, up to 64 characters: letters, digits and _ . : / -.
Up to 20 key-value pairs. Keys up to 40 characters (A-Z a-z 0-9 _ . -), values are stored as strings up to 500 characters.
Returns
Returns the new collection object with 201, or 202 when the outcome is not final yet.
Errors worth handling
| HTTP | Code | Description |
|---|---|---|
400 | currency_ | This currency is not open. |
400 | country_ | This country is not open for this currency. See Coverage. |
400 | operator_ | Unknown operator, or refused by the payment partner for this currency. |
400 | phone_ | The number cannot be read as an international number. |
400 | test_ | Test mode: use one of the test phone numbers of this country. See Testing. |
403 | permission_ | The key’s creator does not have the role required for this operation. |
429 | rate_ | A rate limit was reached. Wait for Retry-After seconds. See Rate limits. |
503 | provider_ | This service is not open in this mode yet. |
curl https://api.monerail.com/v1/collections \
-H "Authorization: Bearer $MONERAIL_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: a1f3c9e2-5b7d-4c21-9f80-3e6b2d1c7a45" \
-d '{
"amount": 5000,
"currency": "XAF",
"phone": "237670000999",
"operator": "MTN",
"customer": {
"name": "Awa Ndiaye"
},
"description": "Order 42",
"client_reference": "order_42",
"metadata": {
"order_id": "42"
}
}'const res = await fetch('https://api.monerail.com/v1/collections', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.MONERAIL_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'a1f3c9e2-5b7d-4c21-9f80-3e6b2d1c7a45',
},
body: JSON.stringify({
amount: 5000,
currency: 'XAF',
phone: '237670000999',
operator: 'MTN',
customer: {
name: 'Awa Ndiaye',
},
description: 'Order 42',
client_reference: 'order_42',
metadata: {
order_id: '42',
},
}),
});
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/collections",
headers={"Authorization": f"Bearer {os.environ['MONERAIL_KEY']}", "Idempotency-Key": "a1f3c9e2-5b7d-4c21-9f80-3e6b2d1c7a45"},
json={
"amount": 5000,
"currency": "XAF",
"phone": "237670000999",
"operator": "MTN",
"customer": {
"name": "Awa Ndiaye",
},
"description": "Order 42",
"client_reference": "order_42",
"metadata": {
"order_id": "42",
},
},
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/collections');
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' => 5000,
'currency' => 'XAF',
'phone' => '237670000999',
'operator' => 'MTN',
'customer' => [
'name' => 'Awa Ndiaye',
],
'description' => 'Order 42',
'client_reference' => 'order_42',
'metadata' => [
'order_id' => '42',
],
]),
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": 5000,
"currency": "XAF",
"phone": "237670000999",
"operator": "MTN",
"customer": map[string]any{
"name": "Awa Ndiaye",
},
"description": "Order 42",
"client_reference": "order_42",
"metadata": map[string]any{
"order_id": "42",
},
})
if err != nil {
panic(err)
}
req, err := http.NewRequest(http.MethodPost, "https://api.monerail.com/v1/collections", 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": 5000,
"currency": "XAF",
"phone": "237670000999",
"operator": "MTN",
"customer": {
"name": "Awa Ndiaye"
},
"description": "Order 42",
"client_reference": "order_42",
"metadata": {
"order_id": "42"
}
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.monerail.com/v1/collections"))
.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/collections")
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: 5000,
currency: "XAF",
phone: "237670000999",
operator: "MTN",
customer: {
name: "Awa Ndiaye",
},
description: "Order 42",
client_reference: "order_42",
metadata: {
order_id: "42",
},
})
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/collections");
request.Headers.Add("Idempotency-Key", "a1f3c9e2-5b7d-4c21-9f80-3e6b2d1c7a45");
request.Content = JsonContent.Create(new
{
amount = 5000,
currency = "XAF",
phone = "237670000999",
@operator = "MTN",
customer = new
{
name = "Awa Ndiaye",
},
description = "Order 42",
client_reference = "order_42",
metadata = new
{
order_id = "42",
},
});
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": "3b8f6c1e-2a47-4d1b-9e35-7c0a9d2f4e61",
"object": "collection",
"livemode": false,
"status": "pending",
"amount": 5000,
"currency": "XAF",
"fee": null,
"net": null,
"customer": {
"name": "Awa Ndiaye",
"phone": "237670000999",
"country": "CM"
},
"operator": "MTN",
"description": "Order 42",
"client_reference": "order_42",
"metadata": {
"order_id": "42"
},
"next_action": {
"type": "customer_approval",
"message": "A payment request was sent. The customer enters their PIN.",
"ussd_code": "*126#"
},
"failure_code": null,
"failure_message": null,
"created_at": "2026-10-30T13:47:06.394Z",
"updated_at": "2026-10-30T13:47:06.394Z"
}Retrieve a collection
Returns the collection. If it is still waiting, Monerail first asks the payment partner for news, so this call can also be used for polling (every few seconds at most).
Key roles: Every role
Path parameters
ID of the collection.
Returns
Returns the collection object.
curl https://api.monerail.com/v1/collections/3b8f6c1e-2a47-4d1b-9e35-7c0a9d2f4e61 \
-H "Authorization: Bearer $MONERAIL_KEY"const res = await fetch('https://api.monerail.com/v1/collections/3b8f6c1e-2a47-4d1b-9e35-7c0a9d2f4e61', {
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/collections/3b8f6c1e-2a47-4d1b-9e35-7c0a9d2f4e61",
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/collections/3b8f6c1e-2a47-4d1b-9e35-7c0a9d2f4e61');
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/collections/3b8f6c1e-2a47-4d1b-9e35-7c0a9d2f4e61", 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/collections/3b8f6c1e-2a47-4d1b-9e35-7c0a9d2f4e61"))
.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/collections/3b8f6c1e-2a47-4d1b-9e35-7c0a9d2f4e61")
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/collections/3b8f6c1e-2a47-4d1b-9e35-7c0a9d2f4e61");
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": "3b8f6c1e-2a47-4d1b-9e35-7c0a9d2f4e61",
"object": "collection",
"livemode": false,
"status": "succeeded",
"amount": 5000,
"currency": "XAF",
"fee": 125,
"net": 4875,
"customer": {
"name": "Awa Ndiaye",
"phone": "237670000999",
"country": "CM"
},
"operator": "MTN",
"description": "Order 42",
"client_reference": "order_42",
"metadata": {
"order_id": "42"
},
"next_action": null,
"failure_code": null,
"failure_message": null,
"created_at": "2026-10-30T13:47:06.394Z",
"updated_at": "2026-10-30T13:47:09.807Z"
}List collections
Newest first.
Key roles: Every role
Query parameters
20Number of objects to return, 1 to 100.
Cursor: the ID of the last object of the previous page.
Returns
Returns a list object: data holds up to limit collection objects, and has_more tells whether more follow.
curl "https://api.monerail.com/v1/collections?limit=10" \
-H "Authorization: Bearer $MONERAIL_KEY"const res = await fetch('https://api.monerail.com/v1/collections?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/collections",
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/collections?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/collections?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/collections?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/collections?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/collections?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/collections",
"data": [
{
"id": "3b8f6c1e-2a47-4d1b-9e35-7c0a9d2f4e61",
"object": "collection",
"livemode": false,
"status": "succeeded",
"amount": 5000,
"currency": "XAF",
"fee": 125,
"net": 4875,
"customer": {
"name": "Awa Ndiaye",
"phone": "237670000999",
"country": "CM"
},
"operator": "MTN",
"description": "Order 42",
"client_reference": "order_42",
"metadata": {
"order_id": "42"
},
"next_action": null,
"failure_code": null,
"failure_message": null,
"created_at": "2026-10-30T13:47:06.394Z",
"updated_at": "2026-10-30T13:47:09.807Z"
}
],
"has_more": false
}Authorize with a code
For collections in requires_otp: sends the one-time code the customer received by SMS, or generated by dialing next_action.ussd_code. The collection moves to processing, then to its final status. In countries where the customer generates the code, 3 attempts are allowed.
Key roles: Owner, Finance or Developer
Path parameters
ID of the collection.
Body
The code, 4 to 8 digits.
Returns
Returns the collection object.
Errors worth handling
| HTTP | Code | Description |
|---|---|---|
400 | otp_ | Wrong, malformed or expired code. |
402 | otp_ | Wrong, malformed or expired code. |
400 | otp_ | This collection is not waiting for a code (already authorized, or no code needed). |
curl https://api.monerail.com/v1/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/authorize \
-H "Authorization: Bearer $MONERAIL_KEY" \
-H "Content-Type: application/json" \
-d '{
"otp": "123456"
}'const res = await fetch('https://api.monerail.com/v1/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/authorize', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.MONERAIL_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
otp: '123456',
}),
});
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/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/authorize",
headers={"Authorization": f"Bearer {os.environ['MONERAIL_KEY']}"},
json={
"otp": "123456",
},
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/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/authorize');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('MONERAIL_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'otp' => '123456',
]),
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{
"otp": "123456",
})
if err != nil {
panic(err)
}
req, err := http.NewRequest(http.MethodPost, "https://api.monerail.com/v1/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/authorize", bytes.NewReader(payload))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("MONERAIL_KEY"))
req.Header.Set("Content-Type", "application/json")
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 = """
{
"otp": "123456"
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.monerail.com/v1/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/authorize"))
.header("Authorization", "Bearer " + System.getenv("MONERAIL_KEY"))
.header("Content-Type", "application/json")
.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/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/authorize")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("MONERAIL_KEY")}"
req["Content-Type"] = "application/json"
req.body = JSON.generate({
otp: "123456",
})
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/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/authorize");
request.Content = JsonContent.Create(new
{
otp = "123456",
});
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": "9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40",
"object": "collection",
"livemode": false,
"status": "processing",
"amount": 2000,
"currency": "XAF",
"fee": null,
"net": null,
"customer": {
"name": "Awa Ndiaye",
"phone": "237690000002",
"country": "CM"
},
"operator": "ORANGE",
"description": "Order 42",
"client_reference": "order_42",
"metadata": {
"order_id": "42"
},
"next_action": null,
"failure_code": null,
"failure_message": null,
"created_at": "2026-10-30T13:47:06.394Z",
"updated_at": "2026-10-30T13:47:09.807Z"
}Resend the code
Asks the operator to send a new code to the customer. Where the customer generates the code with a USSD code, nothing is sent: show next_action.ussd_code again.
Key roles: Owner, Finance or Developer
Path parameters
ID of the collection.
Returns
Returns the collection object.
curl -X POST https://api.monerail.com/v1/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/resend_otp \
-H "Authorization: Bearer $MONERAIL_KEY"const res = await fetch('https://api.monerail.com/v1/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/resend_otp', {
method: 'POST',
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.post(
"https://api.monerail.com/v1/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/resend_otp",
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/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/resend_otp');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('MONERAIL_KEY'),
],
CURLOPT_POSTFIELDS => '',
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.MethodPost, "https://api.monerail.com/v1/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/resend_otp", 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/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/resend_otp"))
.header("Authorization", "Bearer " + System.getenv("MONERAIL_KEY"))
.POST(HttpRequest.BodyPublishers.noBody())
.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/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/resend_otp")
req = Net::HTTP::Post.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.Post, "https://api.monerail.com/v1/collections/9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40/resend_otp");
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": "9e2d4a7b-61c3-4f08-8b5e-2d7f1a3c9b40",
"object": "collection",
"livemode": false,
"status": "requires_otp",
"amount": 2000,
"currency": "XAF",
"fee": null,
"net": null,
"customer": {
"name": "Awa Ndiaye",
"phone": "237690000002",
"country": "CM"
},
"operator": "ORANGE",
"description": "Order 42",
"client_reference": "order_42",
"metadata": {
"order_id": "42"
},
"next_action": {
"type": "otp",
"message": "The customer received a one-time code by SMS."
},
"failure_code": null,
"failure_message": null,
"created_at": "2026-10-30T13:47:06.394Z",
"updated_at": "2026-10-30T13:47:06.394Z"
}