Sandbox Testing
Simulate verification decisions, treasury funding, and deposits, and rehearse every failure path before real money moves.
What the sandbox is
https://sandbox.rasto.co with an sk_test_ key serves the same
endpoints, objects, statuses, and signed webhooks as live. Wallets and
on-chain deliveries run for real on testnets. The parts with no testnet
(bank rails, verification decisions, and exchange rates) are simulated,
and you drive them through endpoints under /api/sandbox/*. Each one
mirrors the resource it acts on, so
POST /api/sandbox/virtual_accounts/{virtual_account_id}/deposits reads like the virtual
account path with sandbox in front.
On the live host every simulator returns 403 sandbox_only.
Sandbox and live, precisely
| Dimension | Sandbox | Live |
|---|---|---|
| Endpoints, objects, statuses, error envelopes | Identical | Identical |
| Webhooks | Delivered and signed like live | Real |
| Verification | You decide, through the simulator. A submitted review stays pending until you do | Decided by the real review |
| Document reading | Every document reads as the same sample values, and none is refused | Read and checked from the images |
| Document asks | The same as live, by customer kind and legal form. See Documents we ask for | The same, plus anything the review asks of one customer later |
| Profile asks | Only the fields required to request an endorsement | The review can ask for any field in the code catalog |
| Bank deposits | Simulated with POST /api/sandbox/virtual_accounts/{virtual_account_id}/deposits | Real bank credits |
| Wallet addresses | Derived the same way, on testnets | Derived the same way, on mainnets |
| Wallet funding | Send testnet tokens to the address | Any on-chain send to the address |
| On-chain delivery and sends | Real transactions on testnets | Real transactions on mainnets |
network on chain objects | testnet | mainnet |
| Treasury inventory | You fund it with POST /api/sandbox/treasury/deposits | Funded operationally |
| Exchange rate | Fixed and deterministic. USD to USDC is at par | USD to USDC at par (1); USD to USDT at the market rate. Your plan's spread applies |
| Simulation endpoints | Available | 403 sandbox_only |
| Account gates | Open once your partner account exists | 403 until your Partner Terms, KYB, and live wallet keys are in place. See Get set up |
Sandbox and live keys, wallet keys, and webhook endpoints are separate, so nothing you register in sandbox carries over to live.
Verification decisions
You make the verification decision in sandbox. After a submit, the
endorsement sits pending until you decide, and the decision applies to the
customer's latest submitted review. The simulator runs the same decision
path a real review takes, so statuses, requirement buckets, and webhooks
behave as they do in live.
curl -s $BASE/api/sandbox/customers/cus_.../verification_decisions \
-H "Api-Key: $KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"decision": "approved"}'
decision | Simulates | What to test |
|---|---|---|
approved | A clean pass | The happy path, and endorsement.approved |
request_for_information | The review asks again for proof_of_address, or registration_document for a business | Your requirement-bucket UI, and the PATCH with a new document that resubmits by itself |
rejected | A final adverse decision | Your terminal-state handling |
The call returns the decided verification round. You can also decide an approved endorsement, which rehearses the review asking for more, or refusing the customer, after approval.
Two refusals apply:
400 verification_not_started: the customer was never submitted. CallPOST /api/customers/{customer_id}/verificationsfirst.400 verification_not_under_review: no review is running, for example while the endorsement is inissues. Answer the ask with a PATCH, which resubmits, then decide again.
Document reading
At submit, sandbox reads every document as fixed sample values, for an individual, a business, and each controller alike. The reading fills only fields you left empty, as in live.
| Document | Field | Sample value |
|---|---|---|
| Identity document | first_name, last_name | Sandbox, User |
| Identity document | date_of_birth | 1990-01-15 |
| Identity document | nationality | US |
| Identity document | Kind, number, issuing country | the kind you sent, else passport; SANDBOX-0001; US |
proof_of_address | address (physical_address for a business) | 123 Sandbox Lane, Sandbox City, 00000, US |
registration_document | legal_name | the legal_name you sent, else Sandbox Company Inc. |
registration_document | business_kind, registration_number, incorporation_date | corporation, SBX-0001, 2020-01-15 |
registration_document | address | 1 Sandbox Plaza, Sandbox City, DE, 19901, US |
proof_of_tax_identification | tax_id, tax_residence_country | 12-3456789, US |
A business created without business_kind therefore reads as a
corporation, and the submit asks for a corporation's documents. Send
business_kind to rehearse another legal form's list.
Sandbox reading never finds a problem with a document, so the document checks only refuse in live.
Treasury funding
Deliveries come from prefunded inventory, and the sandbox treasury starts empty. Fund the asset and chain you are testing first.
curl -s $BASE/api/sandbox/treasury/deposits \
-H "Api-Key: $KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"asset": "usdc", "chain": "solana", "amount": "10000"}'
Without it, a deposit waits in orchestrating for inventory, as it would
in live during an inventory gap, for at most one hour. After that it stays
in orchestrating for our team to resolve, so fund first.
Bank deposits
curl -s $BASE/api/sandbox/virtual_accounts/va_.../deposits \
-H "Api-Key: $KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"amount": "100.00", "sender_name": "ANA SILVA", "remittance_info": "invoice 42"}'
The deposit arrives on the account's primary rail, or on the
payment_rail you name (one of the account's payment_rails), and runs
the real pipeline:
deposit, conversion, payout, with every webhook. Assert on the conversion's
fee_lines and delivered_amount in your integration tests.
Magic amounts
Specific amounts trigger the failure paths.
| Amount | Result |
|---|---|
666.00 | Deposit returned, with the deposit.returned webhook |
777.00 | Deposit on_hold, with the deposit.on_hold webhook |
| anything else | Normal orchestration |
For the two magic amounts, no conversion runs.
Wallet deposits
Wallets have no simulator. Sandbox wallets live on Solana devnet, so you
fund one by sending devnet USDC to its address, from a devnet faucet or
any devnet wallet. The sandbox mint is in the
Signing Guide.
wallet_transaction.created (with kind: "deposit") fires when the
deposit lands, and balances are read from the chain, as in live.
A complete test checklist
Onboarding
Terms accepted, customer created with documents, submitted. Handle
approved, request_for_information plus the PATCH that resubmits,
and rejected. Also assert the 422 verification_incomplete you get
from submitting too early.
Money
Treasury funded, deposit landed. Verify the delivered amount, the
fee_lines, and the tx_hash. Then run 666.00 and 777.00.
Wallets
Create a wallet, fund it with devnet USDC, read the balance back, and run one operation end to end including the verify step. Let one operation expire on purpose.
Webhooks
Every event kind processed idempotently. Replay one with
POST /api/webhook_events/{webhook_event_id}/replay to prove your dedupe works, and
shuffle two events to prove your ordering uses sequence.
Failure hygiene
Retry a POST with the same Idempotency-Key and expect a replay, not a
duplicate. Reuse a key on a different body and expect
422 idempotency_key_reused. Send a POST with no key and expect
400 missing_idempotency_key.
When all five pass in sandbox, work through Going Live.