Sandbox
Same base URL, same routes, same request and response shapes — the integration is byte-identical and only the credential differs. What sandbox adds is four plans that force the failures you cannot otherwise rehearse, which is the part that actually decides whether your integration survives contact with production.
Getting in
A key beginning simway_sk_test_. Ask for one at [email protected]. It arrives with $1,000 of float already on it, and staff can refill it on request.
The float is not money. It appears in no financial figure, it moves no statement row, and nobody invoices you for it.
The four fixture plans
Their plan ids are fixed strings rather than generated ids, precisely so they can be pasted out of this page. All four are priced identically — $19.90 list, 5 GB, 30 days — so you see plausible money and the same three price fields production will give you.
Any other plan ordered with a sandbox key provisions normally after 1.5 seconds — deliberately not instantly, so you cannot ship an integration that has never once handled processing.
What to rehearse, in order
- The happy path. Any real plan. Confirm you handle
processingand only deliver on a terminal state. - A slow order.
pl_sandbox_slowcompletes after 45 seconds, chosen against the 30-second default timeout in most HTTP clients. Let your client time out and then retry with the same idempotency key. If you get one order back, you are safe. If you get two, you would have double-charged a customer in production. - A supplier failure.
pl_sandbox_supplier_error. Confirm your code readsfailure_codeoff the order rather than expecting an HTTP error, and that you notice the refund. - A partial delivery.
pl_sandbox_partialatquantity: 3. Two profiles are yours to deliver; one unit’s money is back. This is the case most integrations get wrong, because it is neither success nor failure. - Running out of money.
pl_sandbox_insufficient_balance. Confirm you mint a new idempotency key after topping up, because the old one is bound to the refusal permanently. Then send the old key again and watch it return the same 402 — that is the behaviour to design around.
What the synthetic profiles look like
Every property here is a decision about what a sandbox artefact should do when it escapes into a real system — because one eventually will.
| Property | Value | Why |
|---|---|---|
| ICCID | 89999…, 19 digits, Luhn-valid | Your own format validation passes, so you are testing your code and not our fixture. The prefix sits in an ITU-reserved private-network range, so it collides with no real operator. |
| SM-DP+ | sandbox.simway.pro | A host we own that can issue nothing. A sandbox code pasted into a real handset fails at DNS rather than reaching somebody else’s SM-DP+. |
| Matching id | SB-… | The tell in a support ticket. Someone will paste one at you eventually. |
| Activation code | A real LPA:1$… string | Built by the same concatenation the live path uses, so one parser handles both. |
| QR code | A real scannable PNG data URL | Same size and quiet zone as live, so your rendering and print layout are tested properly. |
Every way sandbox differs from live
The complete list. Nothing else differs.
| # | Difference |
|---|---|
| 1 | No supplier is ever called, and no real money moves anywhere. |
| 2 | Spend comes off the sandbox float, and GET /balance reports that number. |
| 3 | GET /ledger is ALWAYS empty. Sandbox writes no statement rows, so you cannot exercise statement or reconciliation code here. |
| 4 | balance_after_cents on a later read of a sandbox order is the CURRENT float, not the balance as it stood after that order — there is no ledger row to read it back from. The exact figure is only on the 202 itself. |
| 5 | The sandbox rate band applies regardless of your tier. |
| 6 | The four fixture plans are visible. A live key can never see or order them. |
| 7 | Sandbox keys keep working while an account is suspended. |
| 8 | The forced-failure fixture settles on the first pass rather than spending the 5-attempt budget — so you do not wait 8 minutes to see a failure you asked for. |
| 9 | Prices and margins are real arithmetic on real-looking numbers. The discount and floor logic is identical code. |
| 10 | Fixture plans carry no country code and are global, so they never appear in GET /destinations. |
What you cannot test here
- Statement rendering and reconciliation. There are no ledger rows in sandbox. Plan for this: it is the one piece of work that only gets exercised after you go live.
- Real supplier latency and real failure rates. The fixtures produce the shapes, not the distribution.
- A profile that actually connects. The synthetic SM-DP+ issues nothing.
- Deposits. The rail is live-mode only, and it refuses a sandbox key in three distinguishable ways:
POST /depositsanswers403 forbiddencarryingmode: "sandbox",GET /depositsalways answers an empty page, andGET /deposits/{id}answers404for every well-formeddep_id — the id is parsed before the mode is consulted, so a wrong-kind id is still the ordinary400namingid. Sandbox spend comes off the play-money float, which is refilled on request — a sandbox key that could open an invoice would be sending real cryptocurrency to exercise a code path.
Switching to live
Change the key. That is the whole migration — same base URL, same routes, same shapes. What changes on our side is that money becomes real and a supplier gets called.