Take payments across Rwanda, Uganda and the EU with one API.
One request to collect, one to pay out. We choose the rail — mobile money or bank — from the corridor, so a partner change on our side is never a release on yours.
Sandbox runs the same code as live. No verified business needed to try it.
curl -X POST https://sandbox.inversepay.app/api/b/v1/charges \
-H "Authorization: Bearer $INVERSEPAY_TEST_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"idempotency_key": "order-1001",
"amount_minor": 500000,
"currency": "RWF"
}'Corridors
You never name a partner.
A charge in Rwandan francs and a charge in euros are the same request. Which rail carries it is a routing table on our side, not a release on yours.
Rwanda
RWFMTN and Airtel wallets, and the banks we reach behind them.
Uganda
UGXThe same request shape. A different partner behind it.
Euro area
EURNineteen euro-area countries, paid from the same balance.
The shape of everything
Three rules, and the whole surface follows them.
Learn these once and every endpoint on the API behaves the way you expect it to.
Amounts are minor units
5,000 RWF is 500000. Nothing accepts a decimal amount, because a decimal amount is a rounding argument waiting to happen.
"amount_minor": 500000Writes need an idempotency key
Send the same key again and you get the original object back, not a second one. This is how you retry safely after a timeout.
Idempotency-Key: 01J...Test and live never mix
A test key cannot see live data and a live key cannot see test data — including idempotency keys, so your build-time order references never collide with the real ones.
sk_test_… / sk_live_…Generated, not written
The reference cannot drift.
Every operation page is built from the spec the API produces from its own mounted routes. A page here cannot describe an endpoint that does not exist, and an endpoint that changes shape changes here on the next build.
- Grouped by the object you came for — Charges, Payouts, Invoices — not by URL.
- A playground on every operation that sends a real sandbox request.
- The same spec, exported as a Postman collection with sandbox pre-wired.
- POST
/charges - GET
/charges/{charge_id} - POST
/payouts - GET
/payouts/{payout_id}/timeline - POST
/payment-links - POST
/webhooks/{endpoint_id}/rotate-secret
Sandbox
Fail on purpose, before you fail by accident.
Sandbox is not a separate deployment — it is the same API, the same code paths and the same validation, reached with a key that begins sk_test_. Ask it for an amount and it will decline, time out, or reverse on cue.
999999declined999998timed out999997succeeded, then reversed999996pending review- anything elsesucceeds
Start here
Pick a thread.
Get a test key and send your first charge.
Ten minutes, no verified business, and nothing you do can move real money.