Testing
Test mode in depth: the sandboxes, the test helpers, the dashboard simulators and a full card scenario.
Everything here uses a test key (mr_test_…) and moves no real money. Read Test mode first for how test and live data are separated.
Mobile money in test mode
Test collections and payouts never reach a real operator or a real wallet.
- In every open country, test mode accepts only the test numbers below, and the last digit decides the outcome. Any other number returns
400 test_phone_required. Format errors (400 phone_invalid) behave as in live mode. - In Cameroon and Gambia, Monerails simulates the operation itself. In the other countries, it is sent to a payment partner’s test environment, always with that partner’s own test number, never with the number of a real wallet.
- Write your code for every status:
pending,requires_otp,processing, thensucceeded,failedorexpired. Do not assume a test collection always succeeds. - Collections that succeed credit your test balance. Use that balance to try payouts and settlements.
Test phone numbers
In Cameroon, Côte d’Ivoire, Senegal, Benin, Togo, Burkina Faso, Gabon, Chad, Congo, Central African Republic, Mali, Niger, Guinea-Bissau, Guinea, Gambia and DR Congo, test mode accepts only these numbers, for collections and payouts. Any other number returns 400 test_phone_required, so a test can never reach a real wallet.
| Country | country, currency |
Succeeds | Stays pending |
Fails |
|---|---|---|---|---|
| Cameroon | CM, XAF |
237660000001 |
237660000002 |
237660000003 |
| Côte d’Ivoire | CI, XOF |
2252100000001 |
2252100000002 |
2252100000003 |
| Senegal | SN, XOF |
221700000001 |
221700000002 |
221700000003 |
| Benin | BJ, XOF |
22900000001 |
22900000002 |
22900000003 |
| Togo | TG, XOF |
22800000001 |
22800000002 |
22800000003 |
| Burkina Faso | BF, XOF |
22660000001 |
22660000002 |
22660000003 |
| Gabon | GA, XAF |
24100000001 |
24100000002 |
24100000003 |
| Chad | TD, XAF |
23500000001 |
23500000002 |
23500000003 |
| Congo | CG, XAF |
24200000001 |
24200000002 |
24200000003 |
| Central African Republic | CF, XAF |
23600000001 |
23600000002 |
23600000003 |
| Mali | ML, XOF |
22300000001 |
22300000002 |
22300000003 |
| Niger | NE, XOF |
22700000001 |
22700000002 |
22700000003 |
| Guinea-Bissau | GW, XOF |
245000000001 |
245000000002 |
245000000003 |
| Guinea | GN, GNF |
224600000001 |
224600000002 |
224600000003 |
| Gambia | GM, GMD |
2200000001 |
2200000002 |
2200000003 |
| DR Congo | CD, CDF |
243120000011 |
243120000012 |
243120000013 |
- Use any operator returned by List operators for the country. The final status arrives within a few seconds, by webhook and on the object.
- A number ending in
2never reaches a final status: use it to test your handling of long waits. - Orange Money in Guinea asks for a code: the collection is
requires_otp. Send any 6-digit code with authorize.000000,111111,222222,333333and444444make the collection fail; any other code lets the number decide.
Card test helpers
Two endpoints play the card issuer’s role. They need a test key and the Owner or Finance role.
| Endpoint | Body | Effect |
|---|---|---|
POST /v1/test/cardholders/{id}/verification |
result: approved or rejected; reason (optional, up to 200 characters) |
Decides a verification in in_review. With rejected, reason becomes verification.reason (default: Document unreadable (simulation)). Sends cardholder.kyc_updated. |
POST /v1/test/cards/{id}/authorizations |
amount (US cents), merchant (optional, up to 40 characters), status: approved (default) or declined |
Approved: the payment and its fee are taken from the card and card.transaction is sent. Declined: nothing is charged and card.authorization_declined is sent, with reason Declined (simulation). |
Test payments are simulated by Monerails: they follow the same rules as a real authorization (a frozen card, an insufficient card balance or a spending limit decline the payment, with its decline fee) but never reach the card network. When an approved request is declined by these rules, the response has status: "declined", a decline_code (card_frozen, insufficient_funds or limit_exceeded) and a message.
Test mode limits
Test card funds are shared by every business on Monerails, so card test mode has limits per business:
| Limit | Value |
|---|---|
| Test cards balance + money on test cards | 500 USD at most |
| Test top-up of the cards balance | 10 to 100 USD per operation |
| Active test cards | 10 at most |
| Test cards issued | 20 per calendar month |
Above a limit, the dashboard shows a clear message and the API answers 400 test_limit_reached (429 for the monthly issuance quota). Terminate or withdraw from test cards you no longer use to free room. A test card unused for 30 days, or every test card of a business without test activity for 60 days, is emptied back to the test cards balance and terminated automatically. Need more for a demo? Contact us.
A full card scenario
- Top up the test cards balance. In the dashboard, in test mode, open Cards › Top up the card balance, enter an amount (10 to 100 USD in test mode) and click Credit the test balance (Owner or Finance). The test balance is credited at once; no crypto is involved in test mode.
- Create a cardholder.
curl https://api.monerails.com/v1/cardholders \
-H "Authorization: Bearer $MONERAIL_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "customer", "first_name": "Awa", "last_name": "Ndiaye", "email": "awa@example.com", "phone": "+237670000999", "country": "CM", "birth_date": "1991-02-03", "id_document": "national_id",
"documents": { "front": "data:image/jpeg;base64,/9j/4AAQ…", "back": "data:image/jpeg;base64,/9j/4AAQ…" }}'- Submit the verification. The document photos are required when the cardholder is created (JPEG or PNG in base64: the API checks the format, the size, 4 MB each, and the resolution, 600 × 400 px at least) and reused here; send
documentsagain only to replace them.verification.statusbecomesin_review.
curl https://api.monerails.com/v1/cardholders/5df6acd9-4bac-4e19-869c-fcde6b79f389/verification \
-H "Authorization: Bearer $MONERAIL_KEY" \
-H "Content-Type: application/json" \
-d '{
"id_number": "CM12345678",
"birth_date": "1991-02-03",
"place_of_birth": "Douala",
"address": { "line1": "12 rue Joss", "city": "Douala", "state": "Littoral" },
"documents": { "front": "data:image/jpeg;base64,/9j/4AAQ…", "back": "data:image/jpeg;base64,/9j/4AAQ…" }
}'- Decide the verification. Approve it. To test your rejection path, send
"result": "rejected"with areason, readverification.reason, then submit again.
curl https://api.monerails.com/v1/test/cardholders/5df6acd9-4bac-4e19-869c-fcde6b79f389/verification \
-H "Authorization: Bearer $MONERAIL_KEY" \
-H "Content-Type: application/json" \
-d '{"result": "approved"}'- Wait for
cardholder.kyc_updatedwithverification.statusverified, then issue a card. You get201with anactivecard, or202with apendingcard followed bycard.issuedorcard.issue_failed.
curl https://api.monerails.com/v1/cards \
-H "Authorization: Bearer $MONERAIL_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: test-card-awa-1" \
-d '{"cardholder": "5df6acd9-4bac-4e19-869c-fcde6b79f389", "amount": 1000, "label": "Test card"}'- Simulate payments. An approved payment, then a declined one. Check that your code handles
card.transactionandcard.authorization_declined, and that the declined one changed nothing on the card.
curl https://api.monerails.com/v1/test/cards/ce2f0aea-7f87-4cbd-8383-03129d1253e0/authorizations \
-H "Authorization: Bearer $MONERAIL_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 400, "merchant": "Notion"}'
curl https://api.monerails.com/v1/test/cards/ce2f0aea-7f87-4cbd-8383-03129d1253e0/authorizations \
-H "Authorization: Bearer $MONERAIL_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 90000, "merchant": "Notion", "status": "declined"}'- Manage the card. Fund it, withdraw from it, freeze and unfreeze it. Each action can answer
202: handle the webhook that follows. - Terminate it. Terminate the card and check that
card.terminatedarrives and that its balance is back on the cards balance (Retrieve the balance).
Dashboard simulators
The dashboard, in test mode, has the same simulations behind buttons:
| What | Where | Button (EN / FR) |
|---|---|---|
| Top up the cards balance (Owner or Finance) | Cards › Top up the card balance | Amount, then Credit the test balance / Créditer le solde de test |
| Decide a verification | A cardholder in review | Approve / Valider, Reject / Refuser |
| Pay with a card | A card | Simulate a payment / Simuler un achat |
Test your webhooks
- Create a test endpoint: test events only go to test endpoints, signed with their own secret.
- To receive events on your machine, expose your local server through an HTTPS tunnel with a domain name.
- To test your signature check without Monerails, send yourself an event signed the same way (the
curltab of Verify the signature). - Answer
2xxwithin 10 seconds, then try a failing answer and watch the retries. Use Redeliver an event to replay one.
Before going live
- Every final status is handled:
failedandexpiredcollections,failedpayouts and settlements,card.issue_failed, rejected verifications, declined card payments. - Every
202is followed by a webhook, never by a second creation. - Your tests fail if they receive
"livemode": true.
Then follow Going live.