Sandbox
Two simulated networks that behave like real ones
The sandbox is not a stub that returns success. Each simulated network holds its own account balances, reserves funds when it authorizes, refuses what it cannot fund, and issues its own transaction references instead of echoing yours back.
That last detail matters more than it sounds: reconciliation only means something when the counterparty's report is produced independently. A simulator that repeated our own numbers back would make every reconciliation pass, including the broken ones.
Sandbox and live participants are kept strictly apart. A sandbox payment can never be routed to a real operator, and a live payment can never land on a simulator.
Driving outcomes
The last four digits of an address decide what happens, so any scenario is reproducible on demand rather than something you wait to encounter.
| Suffix | Outcome | Behaviour |
|---|---|---|
| ...0001 | success | Authorizes, executes and credits the recipient. |
| ...0002 | insufficient_funds | Refused at authorization. No funds are held. |
| ...0003 | timeout | The destination never answers. Any hold is released. |
| ...0004 | declined | The destination refuses the payment outright. |
| ...0005 | invalid_recipient | The address does not resolve to an account. |
| ...0006 | pending | Accepted for processing; resolves on a later status check. |
| ...0007 | limit_exceeded | Rejected against a limit on the destination side. |
| ...0008 | unavailable | The rail is temporarily down. |
A full round trip
Sending from a Tanzanian address to a Kenyan one, across two independent simulated networks and a currency boundary.
# 1. Who am I paying, and who can reach them
POST /network/v1/directory/resolve
{ "address": "+254700000001" }
# 2. Price it
POST /network/v1/quotes
{
"sender": { "type": "payment_address", "value": "+255700000001" },
"recipient": { "type": "payment_address", "value": "+254700000001" },
"amount": { "value": 100000, "currency": "TZS" }
}
# -> quote_id, recipient_amount 500000 KES, expires_at
# 3. Commit to that price and execute
POST /network/v1/payments
Idempotency-Key: your-unique-key
{
"sender": { "type": "payment_address", "value": "+255700000001" },
"recipient": { "type": "payment_address", "value": "+254700000001" },
"quote_id": "uln_qte_...",
"auto_execute": true
}
# -> status destination_accepted, settlement obligation created,
# balanced ledger pair written
# 4. Inspect everything that happened
GET /network/v1/payments/uln_tx_...
# -> full state history and the settlement obligations it producedThen certify
When your integration handles all of the above, run the certification suite. It exercises the same scenarios plus the ones that catch a connector telling comfortable lies: authorizing what it cannot fund, reversing money it already delivered, over-refunding, or reporting a timeout as a success.
GET /network/v1/certification/suite
POST /network/v1/certification/runs