Browse and search
Sandbox: PIX, custody and crypto
Create a test key, simulate complete payment flows and verify signed webhooks without moving real funds.
No matching section. Try another term.
Use https://api.luniumpay.com with a lun_test_ key. Every financial operation is simulated. An incoming QR contains SANDBOX:NAO_PAGAVEL: and cannot be paid through a bank. Confirm payment through the simulation API. No withdrawal is sent to an exchange or blockchain.
Start with a test key
curl --fail-with-body https://api.luniumpay.com/keys/sandbox \
-H 'Content-Type: application/json' \
-d '{"name":"My sandbox integration"}'
Save the returned api_key in your backend environment as LUNIUM_API_KEY. Confirm key.sandbox=true using GET /keys/me before creating test operations. Do not put the key in browser code or paste it into an AI conversation.
To run the examples in your own environment, download the English starter kit:
node sandbox.mjs all
# Or Python 3.9+, with no third-party dependencies:
python3 sandbox.py all
The kit creates a key in memory, checks GET /keys/me and rejects production credentials. It also accepts an existing sandbox key through LUNIUM_API_KEY and never prints credentials. Replace all with custody, cashin, payout or cashout to run one journey.
For the public AI connection, call lunium_start_sandbox_demo with a random, stable request_id and flow set to custody, cashin, payout or cashout. Continue with lunium_get_sandbox_demo; it may create the simulated withdrawal already included in the selected journey after confirming the credit. Read the AI integration guide.
Journeys and completion
| Journey | Create | Confirm and track | Success |
|---|---|---|---|
| PIX → BRL custody | POST /cashin/charge, destino: "saldo" |
POST /sandbox/cashin/{cashin_id}/pay, then /cashin/{cashin_id}/status and /saldo |
status=paid, settlement_status=sent; available funds in the correct subaccount |
| PIX → BTC, USDC or another test-catalog route | POST /cashin/charge, destino: "cripto", asset, chain, payout_address |
Same payment simulation and cash-in status lookup | paid and sent; hash begins with sandbox:, with no real transaction |
| Custody → crypto | POST /saldo/sacar-cripto, amount_cents, tax_number, asset, chain, payout_address |
/cashin/{cashin_id}/status |
paid and sent; debit includes fee_cents |
| Custody → PIX | POST /payouts, amount_cents, pix_key |
GET /payouts/{payout_id} |
status=sent; fictitious E2E with ISPB 00000000 |
| Internal transfer | POST /saldo/transferir, from_customer_ref, to_customer_ref, amount_cents |
/saldo/extrato, /saldo/clientes, /saldo/consolidado |
Atomic debit and credit; total custody balance conserved |
| External crypto → PIX | POST /cash-outs, then POST /cash-outs/{cashout_id}/accept |
GET /cash-outs/{cashout_id} |
state=COMPLETED; deposit is also simulated |
Custody does not require a fixed wallet. The balance is BRL: select asset, network, wallet and memo at withdrawal. The kit exercises native BTC, USDC on Polygon and USDC withdrawal on Base. Read the test catalog for other supported routes. Use chain for cash-in and crypto withdrawals; use network for cash-out.
Receive into custody without a wallet
After verifying your sandbox key, create the charge below. The tax ID is synthetic and is only for sandbox use. 100000 BRL cents means R$1,000.00.
curl --fail-with-body https://api.luniumpay.com/cashin/charge \
-H "X-API-Key: $LUNIUM_API_KEY" -H 'Content-Type: application/json' \
-d '{"amount_cents":100000,"payer_tax_number":"12345678901","destino":"saldo","customer_ref":"test-customer","external_id":"sandbox-deposit-001"}'
Save the returned cashin_id as CASHIN_ID, then simulate payment:
curl --fail-with-body -X POST "https://api.luniumpay.com/sandbox/cashin/$CASHIN_ID/pay" \
-H "X-API-Key: $LUNIUM_API_KEY" -H 'Content-Type: application/json' -d '{}'
Query /cashin/{cashin_id}/status until paid and sent, typically after about six seconds. Then read /saldo?customer_ref=test-customer. The credit is net of the key's fees; do not compare it with the gross charge amount. Repeating the payment confirmation does not duplicate credit.
Deterministic cash-in, custody and payout scenarios
Supply sandbox_scenario when creating an operation. Read GET /sandbox for the scenario matrix supported by each flow.
| Scenario | Result |
|---|---|
success |
Success; default when omitted |
delayed |
Approximately 30-second delay before processing continues |
held |
Custody deposit only: credit blocked for 30 seconds, then available |
expired |
Cash-in expires when payment is simulated |
payer_mismatch |
Cash-in refunded for a payer mismatch, without credit or delivery |
refunded |
Cash-in refunded without credit; payout refunded with a single reversal |
failed |
Payment fails; payout debit reversed once |
settlement_failed |
Payment may be confirmed but delivery fails; withdrawal debit and fees reversed |
settlement_uncertain |
Delivery remains incerto; payout remains processing. No reversal before reconciliation |
route_unavailable |
Creation refused without a debit |
provider_unavailable |
Simulated HTTP 503 with no operation created; remains unsuccessful while this scenario is selected |
To resolve uncertainty in sandbox only, call POST /sandbox/operations/{operation_id}/resolve with {"outcome":"sent"} or {"outcome":"failed"}. Repeating the same resolution is idempotent. The operation must belong to this key and already have an uncertain result. There is no simulated confirmation route for production payments.
Also test invalid bodies, asset/network minimums, insufficient funds, concurrent spending, a blocked account, another key's operation ID and an external_id reused with different parameters. Keep one business intent, a stable external_id and the same request body for retries. Charges and crypto withdrawals share an idempotency namespace. After a timeout, recover by operation ID or a list filtered by external_id.
Crypto-to-PIX scenarios
Cash-out uses the last two decimal places of amount, or brl_amount for reverse quotes: .00 success, .01 delay, .02 failure, .03 expiration, .04 limit, .05 slow processing, .06 provider refusal, .07 first acceptance returns 503 and the second succeeds, .08 payer limit. Preview creates no operation. Never send crypto to an address returned by the simulator.
Real webhook delivery for simulated events
Configure a public HTTPS URL you control using PATCH /keys/me and webhook_url. Store webhook_secret securely. Sandbox events use the normal delivery queue and signature and contain sandbox:true. Verify HMAC over the raw request bytes, the timestamp and event_id, and handle duplicate events as described in the webhook manual. Real HTTP delivery does not mean real financial settlement.
GET /sandbox/events shows generated events even without a configured callback. GET /webhooks/deliveries shows delivery attempts; POST /webhooks/deliveries/{event_id}/retry retries a delivery. Events generated before callback configuration are not sent retroactively: create a new operation or use /webhooks/test. Cash-in emits cashin.paid, cashin.settled or the relevant failure. Holds, releases and transfers have their own events. Payout reports its final result; cash-out reports progress and completion.
Configure the receiver before simulating payment. Confirm separately that an event was generated, delivered over HTTP and processed by your application. The starter scripts alone do not prove that your backend handled a webhook.
Persistence, prices and test boundaries
Cash-in, custody, payouts and transfers persist separately from the real financial ledger, including across service restarts. Cash-out is persisted for two hours. There is a limit of 1,000 operations per key for the new journeys and 1,000 cash-outs. POST /sandbox/reset with {"confirm":true} deletes only this key's simulated state; previously queued webhook deliveries retain their history.
Quotes and catalogs are synthetic snapshots identified in responses. Production prices, minimums, availability and risk checks can differ. Sandbox calls no bank, exchange or wallet. Its payout provider fee is 100 BRL cents, reported by /saldo; this is not a live provider price. The payer ladder does not query real personal history; /cashin/limits describes the difference. Hold durations are compressed for testing.
The simulator supports named catalog assets. It does not simulate arbitrary DEX token contracts, bank decoding of dynamic PIX QRs, blockchain execution or private provider policies. Before production, reread /keys/me, /saldo, limits and catalogs with the live key. A pilot using a real PIX payment and a real crypto withdrawal moves actual funds and requires an authorized amount and recipient. A completed demo does not certify provider liquidity or your full production integration.
Integration manual · API reference · English OpenAPI · Human support