> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stablepay.global/llms.txt
> Use this file to discover all available pages before exploring further.

# Sandbox & Testing

> Integrate and test against a full sandbox environment — no real money

# Sandbox & Testing

The sandbox is a complete copy of the StablePay API for building and testing your
integration. It behaves exactly like production — same endpoints, same
request/response shapes — but **nothing is real**: no money moves, no payouts are
sent, no live KYC/AML checks run. Every outcome is deterministic, so you can test
success *and* failure paths reliably.

|                         | URL                                    |
| ----------------------- | -------------------------------------- |
| **Sandbox base URL**    | `https://sandbox-api.stablepay.global` |
| **Production base URL** | `https://api.stablepay.global`         |

<Info>
  Sandbox keys are prefixed `pk_sandbox_` and production keys `pk_live_`. A sandbox
  key only works against the sandbox URL, and vice-versa. StablePay issues your
  sandbox key — reach out to get one for your account.
</Info>

## Test values

Drive specific outcomes with these inputs:

| To get…                                       | Do this                                                                          |
| --------------------------------------------- | -------------------------------------------------------------------------------- |
| **OTP verification to pass**                  | Submit OTP `123456` (works for any mobile/email — no SMS/email is actually sent) |
| **A successful payout**                       | Use a transaction amount with any cents *except* those below (e.g. `100.00`)     |
| **A failed payout**                           | Use an amount ending in `.11` (e.g. `100.11`)                                    |
| **A "deemed" payout** (bank-accepted, no UTR) | Use an amount ending in `.22` (e.g. `100.22`)                                    |
| **KYC verification to be rejected**           | Include the word `reject` in a name / document number (e.g. name `Reject Test`)  |
| **AML screening to flag**                     | Use a wallet address containing `sanction`                                       |

<Note>
  The payout outcome keys off the cents of the amount **you** send (the USD deposit
  amount for a `sell`, or the INR amount for a `payout`) — not the converted total,
  which depends on the live rate.
</Note>

## Simulating a deposit

In production, a `sell` transaction waits for the user's on-chain crypto deposit.
The sandbox has no real blockchain, so you trigger the deposit yourself with a
sandbox-only endpoint. It drives the transaction through its full lifecycle —
deposit detected → confirmed → converted → paid out — firing the same webhooks
production would.

<ParamField path="id" type="string" required>
  The transaction ID returned by `POST /v2/transactions`.
</ParamField>

<ParamField body="amount" type="string">
  Override the deposit amount. Defaults to the transaction's expected amount.
</ParamField>

<ParamField body="confirmations" type="number">
  Confirmations to report. Defaults high enough to confirm immediately; pass a low
  number to leave the deposit merely *detected* (unconfirmed).
</ParamField>

<ParamField body="autocomplete" type="boolean">
  When `true` (default), drives the transaction all the way to a terminal payout.
  Set `false` to stop at `deposit_confirmed`. No-op for `pre_settlement`
  transactions, whose payout already fired at creation.
</ParamField>

```bash theme={null}
curl -X POST https://sandbox-api.stablepay.global/v2/sandbox/transactions/txn_xyz789/simulate-deposit \
  -H "Authorization: Bearer pk_sandbox_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

```json theme={null}
{
  "success": true,
  "simulated": {
    "depositTxHash": "0xSANDBOX...",
    "depositAmount": "100",
    "confirmations": 999,
    "confirmed": true,
    "autocompleted": true
  },
  "data": {
    "transaction": { "status": "completed" },
    "payout": { "status": "SUCCESS", "utr": "..." }
  }
}
```

<Note>
  This endpoint exists only in the sandbox, and only supports `sell` /
  `pre_settlement` deposit flows. For `payout`-type transactions there is no
  deposit to simulate — they pay out immediately on creation.
</Note>

<Warning>
  **Pool settlement:** `simulate-deposit` does not drive `pool_settlement`
  transactions and will return `Could not match a pending deposit for this
      transaction`. In the sandbox their deposit is confirmed automatically at
  creation — call `POST /v2/transactions/{id}/payout` directly to settle them.
</Warning>

## End-to-end walkthrough

A full offramp in the sandbox, using the `sell` flow:

```bash theme={null}
BASE=https://sandbox-api.stablepay.global
KEY=pk_sandbox_YOUR_KEY

# 1. Create an end user
curl -s $BASE/v2/users -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"name":"Test User","mobile":"+919876500000"}'

# 2. Verify mobile (OTP is always 123456)
curl -s -X POST $BASE/v2/users/$USER_ID/verify/mobile/send  -H "Authorization: Bearer $KEY"
curl -s -X POST $BASE/v2/users/$USER_ID/verify/mobile/confirm -H "Authorization: Bearer $KEY" \
  -H 'Content-Type: application/json' -d '{"otp":"123456"}'

# 3. Verify a bank account
curl -s -X POST $BASE/v2/users/$USER_ID/kyc/bank/verify -H "Authorization: Bearer $KEY" \
  -H 'Content-Type: application/json' \
  -d '{"accountNumber":"1234567890","ifsc":"HDFC0001234","accountHolderName":"Test User"}'

# 4. Create a sell transaction
curl -s -X POST $BASE/v2/transactions -H "Authorization: Bearer $KEY" \
  -H 'Content-Type: application/json' -H "Idempotency-Key: $(uuidgen)" \
  -d '{"type":"sell","userId":"'$USER_ID'","amount":"100.00","asset":"USDT","network":"polygon"}'

# 5. Simulate the deposit → drives it to a completed payout
curl -s -X POST $BASE/v2/sandbox/transactions/$TX_ID/simulate-deposit \
  -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{}'
```

Once you're confident, switch the base URL to `https://api.stablepay.global` and
your `pk_live_` key — no other code changes are needed.
