> ## Documentation Index
> Fetch the complete documentation index at: https://ramps-docs-sync-20260320.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Sandbox Testing

> Test your payouts integration in the Grid sandbox environment

## Overview

The Grid sandbox environment allows you to test your payouts integration without moving real money. All API endpoints work the same way in sandbox as they do in production, but money movements are simulated and you can control test scenarios using special test values.

## Getting Started with Sandbox

### Sandbox Credentials

To use the sandbox environment:

1. Go to [app.lightspark.com](https://app.lightspark.com), create an account, and generate your sandbox API keys from the dashboard.
2. Add your sandbox API token and secret to your environment variables.
3. Use the normal production base URL: `https://api.lightspark.com/grid/2025-10-13`
4. Authenticate using your sandbox token with HTTP Basic Auth

## Simulating Money Movements

### Funding Internal Accounts

In production, internal accounts are funded by following the payment instructions (bank transfer, wire, etc.). In sandbox, you can instantly add funds to any internal account using the following endpoint:

```bash theme={null}
POST /sandbox/internal-accounts/{accountId}/fund

{
  "amount": 100000  # $1,000 in cents
}
```

**Example:**

```bash theme={null}
curl -X POST https://api.lightspark.com/grid/2025-10-13/sandbox/internal-accounts/InternalAccount:abc123/fund \
  -u "sandbox_token_id:sandbox_token_secret" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 100000
  }'
```

This endpoint returns the updated `InternalAccount` object with the new balance.

Alternatively, you can also fund internal accounts using the `/quotes` or `/transfer-in` endpoints as described below.

## Testing Transfer Scenarios

### Adding Test External Accounts

The flows for creating external accounts in sandbox are the same as in production. The **last 3 digits** of an external account's primary identifier (account number, IBAN, CLABE, Spark wallet address, etc.) determine the test scenario when that account is used in transfers or quotes. For identifiers with a domain part (e.g. PIX email keys), append the test digits to the username portion — for example, `testuser.002@pix.com.br`.

### Beneficiary name verification

For account types that support beneficiary name verification, you can simulate different verification outcomes in sandbox. Use account identifiers with a `1xx` suffix to trigger verification scenarios (this range is reserved for verification and does not conflict with transfer or quote test patterns):

| Suffix        | `beneficiaryVerificationStatus` | Behavior                                                             |
| ------------- | ------------------------------- | -------------------------------------------------------------------- |
| **102**       | `NOT_MATCHED`                   | Account is valid but name does not match                             |
| **103**       | `PARTIAL_MATCH`                 | Account is valid, name is a fuzzy match                              |
| **104**       | `PENDING`                       | Verification still in progress                                       |
| **105**       | *(error)*                       | Returns `400` — invalid account                                      |
| **106**       | `UNSUPPORTED`                   | Payment rail does not support name verification                      |
| **107**       | `CHECKED_BY_RECEIVING_FI`       | Verification deferred to receiving financial institution (e.g., ACH) |
| **109**       | *(error)*                       | Returns `500` — simulated API error                                  |
| **Any other** | `MATCHED`                       | Account is valid, name matches exactly                               |

### Testing Transfer-In (Pull from External Account)

When you call `/transfer-in` with an external account created using test patterns, the transfer will complete instantly in sandbox with the behavior determined by the account number:

```bash theme={null}
POST /transfer-in

{
  "source": {
    "accountId": "ExternalAccount:abc123"  // Uses test pattern from creation
  },
  "destination": {
    "accountId": "InternalAccount:xyz789"
  },
  "amount": 10000  // $100 in cents
}
```

| Suffix        | Behavior                                                  |
| ------------- | --------------------------------------------------------- |
| **002**       | Insufficient funds — transfer fails immediately           |
| **003**       | Account closed/invalid — transfer fails immediately       |
| **004**       | Transfer rejected — bank rejects the transfer             |
| **005**       | Timeout/delayed failure — stays pending \~30s, then fails |
| **Any other** | Success — transfer completes normally                     |

### Testing Transfer-Out (Push to External Account)

Transfer-out works the same way - the destination external account's test pattern determines the outcome:

```bash theme={null}
POST /transfer-out

{
  "source": {
    "accountId": "InternalAccount:xyz789"
  },
  "destination": {
    "accountId": "ExternalAccount:abc123"  // Uses test pattern
  },
  "amount": 10000
}
```

The transfer will instantly simulate the bank transfer process and complete with the appropriate status based on the external account's test pattern.

## Testing Cross-Currency Quotes

When creating a quote with an external account destination, the account number suffix determines the payment outcome after quote execution:

| Suffix        | Behavior                                                               |
| ------------- | ---------------------------------------------------------------------- |
| **002**       | Quote execution failed                                                 |
| **003**       | Long payment — completes after approximately 6 minutes                 |
| **004**       | Counterparty delivery failed                                           |
| **005**       | Receiving bank returned payment (completes then transitions to failed) |
| **006**       | User cancellation                                                      |
| **007**       | Payout and refund failed                                               |
| **Any other** | Successful payment                                                     |

### Creating Quotes with Test Accounts

When creating quotes with test external accounts, first create an external account with a test account pattern, then reference it in the quote:

```bash theme={null}
# Step 1: Create the external account with a test pattern
POST /customers/external-accounts

{
  "customerId": "Customer:123",
  "currency": "EUR",
  "accountInfo": {
    "accountType": "IBAN_ACCOUNT",
    "iban": "DE89370400440532013003",
    "beneficiary": {
      "beneficiaryType": "INDIVIDUAL",
      "fullName": "Test User"
    }
  }
}

# Step 2: Create the quote using the external account ID
POST /quotes

{
  "source": {
    "sourceType": "ACCOUNT",
    "accountId": "InternalAccount:abc123"
  },
  "destination": {
    "destinationType": "ACCOUNT",
    "accountId": "ExternalAccount:...",
    "currency": "EUR"
  },
  "lockedCurrencySide": "SENDING",
  "lockedCurrencyAmount": 100000
}
```

The IBAN ending in `003` triggers the slow payment test pattern.

### Executing Quotes in Sandbox

For quotes from an external account source, execute as in production via `/quotes/{quoteId}/execute`. The sandbox will:

1. Instantly process the currency conversion
2. Apply the test behavior based on any external accounts involved
3. Update transaction statuses immediately (no waiting for bank processing)
4. Trigger webhooks for state changes

For quotes with payment instructions (no source account), use the existing `/sandbox/send` endpoint to simulate payment:

```bash theme={null}
POST /sandbox/send

{
  "reference": "UMA-Q12345-REF",  // From quote payment instructions
  "currencyCode": "USD",
  "currencyAmount": 100000
}
```

## Testing Webhooks

All webhook events fire normally in sandbox. To test your webhook endpoint:

1. Configure your webhook URL in the dashboard
2. Perform actions that trigger webhooks (transfers, quote execution, etc.)
3. Receive webhook events at your endpoint
4. Verify signature using the sandbox public key

You can also manually trigger a test webhook:

```bash theme={null}
POST /webhooks/test

{
  "url": "https://your-app.com/webhooks"
}
```

## Common Testing Workflows

### Complete Payout Flow Test

Here's a complete test workflow for a USD → EUR payout:

1. **Create customer and internal accounts** (via regular API)

2. **Fund customer's USD internal account:**
   ```bash theme={null}
   POST /sandbox/internal-accounts/InternalAccount:customer-usd/fund
   { "amount": 100000 }  # $1,000
   ```

3. **Create a test external EUR account:**
   ```bash theme={null}
   POST /customers/external-accounts
   # Use default account number for success case
   ```

4. **Create and execute a quote:**
   ```bash theme={null}
   POST /quotes
   # USD internal → EUR external

   POST /quotes/{quoteId}/execute
   ```

5. **Verify transaction status and webhooks**

### Testing Error Scenarios

Test each failure mode systematically:

```bash theme={null}
# 1. Test insufficient funds
# Create external account ending in 002
POST /customers/external-accounts { "accountNumber": "000000002" }

# Attempt transfer-in - should fail immediately
POST /transfer-in

# 2. Test account closed
# Create external account ending in 003
POST /customers/external-accounts { "accountNumber": "000000003" }

# Attempt transfer-out - should fail immediately
POST /transfer-out

# 3. Test timeout scenario
# Create external account ending in 005
POST /customers/external-accounts { "accountNumber": "000000005" }

# Attempt transfer - should pend then fail after ~30s
POST /transfer-in
# Check status immediately - will show PENDING
GET /transactions/{transactionId}
# Wait 30s, check again - will show FAILED
```

## Sandbox Limitations

While sandbox closely mimics production, there are some differences:

* **Instant settlement**: All transfers complete immediately (success cases) or fail immediately (error cases), except timeout scenarios (005)
* **No real bank validation**: Account numbers aren't validated against real banking networks
* **Simplified KYC**: KYC processes are simulated and complete instantly. You must add customers via the `/customers` endpoint, rather than using the KYC link flow.
* **Fixed exchange rates**: Currency conversion rates may not reflect real-time market rates.

<Warning>
  Do not try sending money to any sandbox addresses or accounts. These are not real addresses and will not receive money.
</Warning>

## Moving to Production

When you're ready to move to production:

1. Generate production API tokens in the dashboard
2. Swap those credentials for the sandbox credentials in your environment variables
3. Remove any sandbox-specific test patterns from your code
4. Configure production webhook endpoints
5. Test with small amounts first

## Next Steps

* Review [Webhooks](/payouts-and-b2b/platform-tools/webhooks) for event handling
* Check out the [Postman Collection](/payouts-and-b2b/platform-tools/postman-collection) for API examples
* See [Error Handling](/payouts-and-b2b/payment-flow/error-handling) for production error strategies
