Get started
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
Collections and payouts go to the payment partner’s sandbox, not to real operators.
- In most countries, the sandbox decides how a test collection proceeds (approval on the phone or a code) and which final status it reaches. Send numbers in a valid format for the country (for example
237670000999for Cameroon). Format errors (400 phone_invalid) behave as in live mode. - In the countries listed under Test phone numbers, only the test numbers are accepted in test mode, and the last digit decides the outcome.
- 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 Gabon, Chad, Congo, Central African Republic, Mali, Niger, Guinea 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 |
|---|---|---|---|---|
| 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 | GN, GNF |
224600000001 |
224600000002 |
224600000003 |
| 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). |
A full card scenario
- Top up the test cards balance. In the dashboard, in test mode, open Cards › Top up and click Simulate the deposit (Owner or Finance). It simulates a USDT deposit on TRON (TRC20).
- Create a cardholder.
curl https://api.monerail.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"}'- Submit the verification. Send JPEG or PNG images in base64: the API checks the format and the size (4 MB each).
verification.statusbecomesin_review.
curl https://api.monerail.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" },
"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.monerail.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.monerail.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.monerail.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.monerail.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 | Simulate the deposit / Simuler le dépôt |
| 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 Monerail, 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.