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

# Sandbox Testing

Superbank provides sandbox endpoints that let you simulate payment and settlement status transitions in non-production environments. This enables full end-to-end testing without waiting for real payment processing.

## Overview

In sandbox environment, the Superbank API exposes additional endpoints under `/v0/sandbox/` that allow you to:

* Transition payment statuses (e.g., simulate a payout completing)
* Transition settlement request statuses (e.g., force a settlement to complete)
* Trigger outbound webhooks and realtime updates for each transition

These endpoints are **blocked in production** and require your standard API key for authentication.

## Available Endpoints

### Get Sandbox Info

Check if sandbox mode is available in your environment.

```bash cURL theme={null}
curl --request GET \
  --url https://api-sandbox.superbank.co/v0/sandbox/info \
  --header 'X-Api-Key: YOUR_API_KEY'
```

```json Response theme={null}
{
  "environment": "sandbox",
  "sandbox_enabled": true
}
```

### Transition Payment Status

Simulate a payment status change. This triggers the full webhook pipeline, including outbound webhooks to your registered endpoints and realtime updates.

```bash cURL theme={null}
curl --request PATCH \
  --url https://api-sandbox.superbank.co/v0/sandbox/payments/{payment_id}/status \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "status": "COMPLETED"
  }'
```

```json Response theme={null}
{
  "id": "d4e5f6a7-b8c9-4d0e-a1f2-3b4c5d6e7f8a",
  "previous_status": "PROCESSING",
  "status": "COMPLETED",
  "updated_at": "2026-01-20T15:25:00.000Z"
}
```

### Transition Settlement Request Status

Simulate a settlement request status change. This triggers outbound webhooks and realtime updates.

```bash cURL theme={null}
curl --request PATCH \
  --url https://api-sandbox.superbank.co/v0/sandbox/settlement-requests/{settlement_id}/status \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "status": "SETTLEMENT_COMPLETED"
  }'
```

```json Response theme={null}
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "previous_status": "FUNDS_SENT",
  "status": "SETTLEMENT_COMPLETED",
  "updated_at": "2026-01-20T15:25:00.000Z"
}
```

## Payment Status Values

| Status         | Description                               |
| -------------- | ----------------------------------------- |
| `PENDING`      | Payment created, awaiting processing      |
| `PROCESSING`   | Payment is being processed                |
| `COMPLETED`    | Payment successfully completed            |
| `FAILED`       | Payment failed (provide `failure_reason`) |
| `CANCELED`     | Payment was canceled                      |
| `UNDER_REVIEW` | Payment flagged for compliance review     |

## Settlement Request Status Values

| Status                          | Description                                                                                                 |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `SETTLEMENT_COMPLETED`          | Payout to destination completed                                                                             |
| `REQUEST_COMPLETED`             | Full settlement cycle complete (after reconciliation)                                                       |
| `AWAITING_PAYIN_RECONCILIATION` | `REQUEST_COMPLETED` was called but the inbound PAYIN has not yet been observed; auto-resolves once it lands |
| `PAYIN_AMOUNT_MISMATCH`         | Inbound PAYIN landed but its amount does not match the settlement amount                                    |
| `PAYOUT_FAILED`                 | Payout to destination failed                                                                                |
| `PAYOUT_UNDER_REVIEW`           | Payout flagged for review                                                                                   |
| `PAYOUT_CANCELED`               | Payout was canceled                                                                                         |
| `PAYIN_FAILED`                  | Inbound payment failed                                                                                      |
| `PAYIN_UNDER_REVIEW`            | Inbound payment flagged for review                                                                          |

## Testing a Full On-Ramp Flow

Here is a complete example testing the `STABLECOIN_TO_STABLECOIN` flow end-to-end.

### Step 1: Create a Settlement Request

```bash cURL theme={null}
curl --request POST \
  --url https://api-sandbox.superbank.co/v0/settlement-requests \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "type": "STABLECOIN_TO_STABLECOIN",
    "payment_reason": "REMITTANCES",
    "amount": 10,
    "destination": {
      "currency": "USDC",
      "rail": "SOLANA",
      "is_third_party": false,
      "wallet_address": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
      "beneficiary": {
        "type": "INDIVIDUAL",
        "first_name": "John",
        "last_name": "Doe",
        "address": { "country_code": "US" }
      }
    }
  }'
```

Save the `id` from the response - you'll need it for subsequent steps.

### Step 2: Transition to FUNDS\_SENT

Notify that you have collected the funds externally.

```bash cURL theme={null}
curl --request PUT \
  --url https://api-sandbox.superbank.co/v0/settlement-requests/{settlement_id} \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "status": "FUNDS_SENT",
    "transaction_id": "your_external_txn_123"
  }'
```

This triggers the creation of a payout payment. Retrieve the payment ID from the `outbound_payment.id` field in the response.

### Step 3: Complete the Payout Payment

Use the sandbox endpoint to simulate the payout completing. Transition through `PROCESSING` first, then `COMPLETED`.

```bash cURL theme={null}
# First: PROCESSING
curl --request PATCH \
  --url https://api-sandbox.superbank.co/v0/sandbox/payments/{payment_id}/status \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{"status": "PROCESSING"}'

# Then: COMPLETED
curl --request PATCH \
  --url https://api-sandbox.superbank.co/v0/sandbox/payments/{payment_id}/status \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{"status": "COMPLETED"}'
```

After the payment completes, the settlement request automatically transitions to `SETTLEMENT_COMPLETED`.

### Step 4: Complete Reconciliation

Finalize the settlement by providing the inbound transaction hash.

```bash cURL theme={null}
curl --request PUT \
  --url https://api-sandbox.superbank.co/v0/settlement-requests/{settlement_id} \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "status": "REQUEST_COMPLETED",
    "transaction_hash": "your_payin_transaction_hash"
  }'
```

### Step 5: Verify Final Status

```bash cURL theme={null}
curl --request GET \
  --url https://api-sandbox.superbank.co/v0/settlement-requests/{settlement_id} \
  --header 'X-Api-Key: YOUR_API_KEY'
```

The settlement should now show `status: "REQUEST_COMPLETED"`.

<Note>
  **If you see `AWAITING_PAYIN_RECONCILIATION` in Step 4's response**, your
  `transaction_hash` was accepted but the matching inbound PAYIN had not
  been indexed yet. Don't retry the call — the request will resolve to
  `REQUEST_COMPLETED` (or `PAYIN_AMOUNT_MISMATCH`) automatically and emit a
  `settlement_request.updated` webhook once the PAYIN lands. See [Real
  Time On-Ramping → Step 6](/platform/guides/real-time-on-ramping#step-6-reconcile-via-request_completed)
  for the full explanation.
</Note>

## Testing Failure Scenarios

You can also test error handling by transitioning to failure states.

### Simulate a Payment Failure

```bash cURL theme={null}
curl --request PATCH \
  --url https://api-sandbox.superbank.co/v0/sandbox/payments/{payment_id}/status \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "status": "FAILED",
    "failure_reason": "Insufficient liquidity in destination pool"
  }'
```

### Simulate a Payout Under Review

```bash cURL theme={null}
curl --request PATCH \
  --url https://api-sandbox.superbank.co/v0/sandbox/settlement-requests/{settlement_id}/status \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: YOUR_API_KEY' \
  --data '{
    "status": "PAYOUT_UNDER_REVIEW"
  }'
```

## Simulating a Payin

For settlement flows that require an inbound payment (e.g., stablecoin-to-stablecoin), you need to send real test tokens on a blockchain devnet. The sandbox environment uses **Solana Devnet** for USDC testing.

<Note>
  Devnet tokens have no monetary value. They are free test tokens used exclusively for development
  and testing.
</Note>

### Prerequisites

1. **Install Solana CLI**

```bash theme={null}
sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"
export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"
```

2. **Create a devnet wallet**

```bash theme={null}
solana-keygen new --outfile ~/.config/solana/devnet.json
solana config set --url devnet --keypair ~/.config/solana/devnet.json
solana address  # Note this address
```

3. **Get SOL for gas fees**

```bash theme={null}
solana airdrop 1
```

If the airdrop is rate-limited, use [faucet.solana.com](https://faucet.solana.com/).

4. **Get USDC from Circle Faucet**

* Go to [faucet.circle.com](https://faucet.circle.com/)
* Select **Solana** and **Devnet**
* Paste your wallet address
* You'll receive 20 USDC (can request every 2 hours)

Devnet USDC Mint: `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU`

### Sending a Test PAYIN

Once you have devnet USDC, send tokens to the prefunded wallet address returned in your settlement request's `payment_instructions.wallet_address` field.

```javascript Node.js theme={null}
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
import { getOrCreateAssociatedTokenAccount, transfer, getMint } from '@solana/spl-token';
import bs58 from 'bs58';

const SOLANA_RPC = 'https://api.devnet.solana.com';
const USDC_MINT = '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU';

async function sendUSDC(privateKey, toWallet, amount) {
  const connection = new Connection(SOLANA_RPC, 'confirmed');
  const payer = Keypair.fromSecretKey(bs58.decode(privateKey));
  const mint = new PublicKey(USDC_MINT);
  const destination = new PublicKey(toWallet);

  // Get token accounts
  const mintInfo = await getMint(connection, mint);
  const sourceAccount = await getOrCreateAssociatedTokenAccount(
    connection,
    payer,
    mint,
    payer.publicKey,
  );
  const destAccount = await getOrCreateAssociatedTokenAccount(connection, payer, mint, destination);

  // Send tokens
  const rawAmount = BigInt(amount * 10 ** mintInfo.decimals);
  const signature = await transfer(
    connection,
    payer,
    sourceAccount.address,
    destAccount.address,
    payer,
    rawAmount,
  );

  console.log(`Transaction: ${signature}`);
  console.log(`Explorer: https://explorer.solana.com/tx/${signature}?cluster=devnet`);
  return signature;
}

// Usage: sendUSDC("your_base58_private_key", "wallet_address", 10);
```

```python Python theme={null}
from solders.keypair import Keypair
from solana.rpc.api import Client
from spl.token.instructions import transfer_checked, TransferCheckedParams
from solana.transaction import Transaction
import base58

SOLANA_RPC = "https://api.devnet.solana.com"
USDC_MINT = "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"

def send_usdc(private_key: str, to_wallet: str, amount: float):
    client = Client(SOLANA_RPC)
    payer = Keypair.from_bytes(base58.b58decode(private_key))

    # Build and send SPL token transfer
    # See: https://spl.solana.com/token for full implementation
    print(f"Send {amount} USDC to {to_wallet}")
```

Save the transaction signature - you'll need it for the `transaction_hash` field in the reconciliation step (`REQUEST_COMPLETED`).

## Webhook Events

Each sandbox transition triggers the same webhook events as production, with the **same payload shape**. Test handlers wired to sandbox transitions will receive the same fields they will see in production.

| Transition               | Webhook Event                |
| ------------------------ | ---------------------------- |
| Payment status change    | `payment.updated`            |
| Settlement status change | `settlement_request.updated` |

Payload shape mirrors the production trigger path (`type`, `status`, `amount`, `external_id`, `metadata`, `source`, `destination`, `updated_at`). See the [Webhooks guide](/platform/guides/webhook-guide) for the canonical envelope and per-event field list.

Make sure you have a [webhook endpoint configured](/platform/guides/webhook-guide) to receive these events.

## Environment Availability

| Environment | Sandbox Endpoints         |
| ----------- | ------------------------- |
| Sandbox     | Available                 |
| Production  | **Blocked** (returns 403) |

<Card title="Webhooks" icon="bell" href="/platform/guides/webhook-guide">
  Learn how to set up, verify, and debug webhook endpoints.
</Card>

<Card title="Real Time On-Ramping" icon="bolt" href="/platform/guides/real-time-on-ramping">
  Complete guide to the on-ramping settlement flow.
</Card>
