Testing in Sandbox
Test every flow with fake data: qp_test_ keys, payin and payout scenarios, manual control, and the built-in webhook receiver.
Test everything with fake data
The Sandbox environment is a complete copy of the API that runs on play money. You can act as a merchant from start to finish (create a key, create payins, create payouts, receive webhooks, read balances and journals) without a real customer, bank or AntaraPay operator. Sandbox simulates all of them for you.
Sandbox only
qp_test_ keys, base URL https://sandbox-api.antarapay.com). The /api/v1/sandbox/* endpoints return 404 in Production, and no real money ever moves.Step by step
- Sign in to the dashboard and switch to Sandbox (environment switch in the header).
- Open Integration and generate your webhook key pair and an API key. Sandbox keys start with
qp_test_; copy the key, it is shown once. Give it thepayins:read,payins:write,payouts:readandpayouts:writescopes. - Your account is credited with play money automatically (IDR 10,000,000 the first time). Check it with
GET /api/v1/balances, and top it up withPOST /api/v1/sandbox/balance/topup. - Create a payin and a payout as described in the Payin and Payout guides. Sandbox settles them by itself after about 10 seconds, based on the amount or account number you send (see the tables below).
- Receive the webhooks on your own server, or use the built-in receiver if you do not have one yet.
curl "https://sandbox-api.antarapay.com/api/v1/sandbox" \
-H "Authorization: Bearer $ANTARAPAY_API_KEY"GET /api/v1/sandbox returns the autopilot settings, the starter balance, and every scenario below with an example amount or account number.
Payin scenarios
The outcome is chosen by the last three digits of the amount. Any other amount is paid successfully.
| Amount ends in | Example | Result |
|---|---|---|
| any other value | 15000 | Paid: status SUCCEEDED, the net amount reaches your balance. |
400 | 10400 | Fails: status FAILED. |
500 | 10500 | Expires: status EXPIRED. |
600 | 10600 | Stays AWAITING_PAYMENT so you can control it by hand. |
Channels with delayed settlement (for example QRIS_GOPAY) are paid and then released immediately, so you do not wait for the settlement period.
Payout scenarios
The outcome is chosen by the last four digits of recipient.account_number. Your balance is reserved when the payout is created and released again when it fails or is rejected.
| Account number ends in | Example | Result |
|---|---|---|
| any other value | 1234567890 | Approved, sent and completed: status SUCCEEDED with a SBX-TRF-... transfer reference. |
0000 | 1234560000 | Rejected: status REJECTED. |
9999 | 1234569999 | Fails at the bank: status FAILED. |
8888 | 1234568888 | Stays PENDING_APPROVAL so you can control it by hand. |
Control it by hand
Use these endpoints with the same API key to play the customer, the operator and the bank yourself. They only affect your own payins and payouts, do not need an Idempotency-Key, and respond with 409 when the payin or payout is already in a final state.
| Endpoint | Scope | What it does |
|---|---|---|
GET /sandbox | any | Settings, scenarios and examples. |
POST /sandbox/balance/topup | payins:write | Adds play money. Body { "amount": "500000" }, between 1,000 and 10,000,000. |
POST /sandbox/settlements/release | payins:write | Makes every waiting settlement available now. |
POST /sandbox/payins/{id}/pay | payins:write | The customer pays: SUCCEEDED. |
POST /sandbox/payins/{id}/fail | payins:write | The payment fails: FAILED. |
POST /sandbox/payins/{id}/expire | payins:write | The payin expires: EXPIRED. |
POST /sandbox/payouts/{id}/complete | payouts:write | Approves, sends and completes the payout. |
POST /sandbox/payouts/{id}/fail | payouts:write | The bank transfer fails; the funds are released. |
POST /sandbox/payouts/{id}/reject | payouts:write | The payout is rejected; the funds are released. |
curl -X POST "https://sandbox-api.antarapay.com/api/v1/sandbox/payins/$PAYIN_ID/pay" \
-H "Authorization: Bearer $ANTARAPAY_API_KEY"Built-in webhook receiver
No server to receive webhooks yet? Let Sandbox be your server. It receives the events, verifies both the Ed25519 and the HMAC signature, and keeps what it got so you can inspect it.
| Endpoint | Scope | What it does |
|---|---|---|
POST /sandbox/webhook/receiver | payins:write | Points your webhook URL at the built-in receiver. |
GET /sandbox/webhook/inbox | payins:read | The events received, with signature_valid, verified_with and the full body. |
POST /sandbox/webhook/test | payins:write | Sends a signed sample event (type payin, reference TEST-WEBHOOK) to your webhook URL. |
Each event has type set to payin or payout; read data.status to know what happened (see the webhook guide).
A complete test run
# 1. Receive webhooks in the built-in inbox
curl -X POST "$BASE/api/v1/sandbox/webhook/receiver" -H "Authorization: Bearer $KEY"
# 2. Create a payin that will be paid by itself
curl -X POST "$BASE/api/v1/payins" \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"reference":"ORD-1","amount":"15000","currency":"IDR","channel":"QRIS_QOIN"}'
# 3. About 10 seconds later: status SUCCEEDED, balance increased
curl "$BASE/api/v1/payins/$PAYIN_ID" -H "Authorization: Bearer $KEY"
curl "$BASE/api/v1/balances" -H "Authorization: Bearer $KEY"
# 4. Create a payout that will complete by itself
curl -X POST "$BASE/api/v1/payouts" \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"reference":"PO-1","amount":"40000","currency":"IDR","recipient":{"type":"END_USER","bank_code":"BCA","account_number":"1234567890","account_name":"TEST USER"}}'
# 5. Read the webhooks you received and the journal
curl "$BASE/api/v1/sandbox/webhook/inbox" -H "Authorization: Bearer $KEY"
curl "$BASE/api/v1/journals" -H "Authorization: Bearer $KEY"Failure paths too
400 and 500, and with account numbers ending in 0000 and 9999, to make sure your system handles failed, expired and rejected results and that balances come back.