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

# Sandbox and testing

> How the Nuvion sandbox differs from production, how to fund a sandbox account, and how to test webhooks without real traffic.

The sandbox is a separate environment with its own data, keys and dashboard. No real funds move in sandbox. Build and test against it before you switch to production.

## Environment

| | Sandbox | Production |
| - | - | - |
| Base URL | `https://api.nuvion.dev` | `https://api.nuvion.co` |
| Dashboard | `https://app.nuvion.dev/dashboard/settings` | `https://app.nuvion.co/dashboard/settings` |

Sandbox and production keys, webhooks and data are separate. Keys in both environments begin with `nv_live_`, so the prefix doesn't tell you which environment a key belongs to. A key only works against the environment whose dashboard issued it. Send each key to the matching base URL, and label your stored keys `sandbox` or `production`.

## Sandbox behavior

| Area | Sandbox behavior |
| - | - |
| KYC documents | Document content isn't verified. Any base64-encoded file is accepted when `meta.file_type` is a supported MIME type. See [Upload a KYC document](https://docs.nuvion.co/api-reference/entities.md#upload-a-kyc-document). |
| Entity review | An individual entity moves from `pending` to `approved` within seconds of submission. The `verification_status` of the document and identification stays `pending`. |
| Funding | A `GBP` or `EUR` Open Banking funding session credits the account once you complete the sandbox bank authentication flow. The stablecoin checkout doesn't simulate the on-chain deposit, so it doesn't credit funds. |
| Stablecoin wallets | A USDC account accepts one wallet. Creating a second returns `409 error_duplicate_resource`. |
| USD account details | Creating account details for a USD account can return `503 error_system_dependency_unavailable`. Retry later, or use an account in another currency. |
| Webhooks | Sandbox events go only to your sandbox webhook URLs. Use separate endpoints for sandbox and production. |

## Fund a sandbox account

Create a `GBP` or `EUR` Open Banking funding session, either through the API or the dashboard. Open the `checkout_url`, complete the sandbox bank flow, and wait for the `funding_sessions.updated` webhook with `status: "settled"`. See [Funding sessions](https://docs.nuvion.co/guides/funding-session.md) for the request.

## Test webhook delivery

Send a test event to a webhook you created. Nuvion delivers it to the webhook URL with a valid signature, so you can test your signature check and your handler without waiting for real activity.

```bash curl theme={null}
curl -X POST https://api.nuvion.dev/webhook-tests \
  -H "Authorization: Bearer $NUVION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_webhook_id": "01HXYZ9001ABCDEFGHJKMNPQRS",
    "event_group": "inflows",
    "event_name": "completed"
  }'
```

```json 200 OK theme={null}
{
  "status": "success",
  "message": "Test Webhook sent successfully"
}
```

The delivered body carries only the webhook ID. It does not contain a full resource:

```json Delivered body theme={null}
{
  "event": "inflows.completed",
  "data": {
    "webhook_id": "01HXYZ9001ABCDEFGHJKMNPQRS"
  }
}
```

Test events use the same headers and signature scheme as real events. See [Verifying webhook signatures](https://docs.nuvion.co/webhooks/overview.md#verifying-webhook-signatures) for the check, and [Send a test event](https://docs.nuvion.co/api-reference/webhooks.md#send-a-test-event) for the endpoint reference.

## Test ID document details

The sandbox validates the shape of the identity document details you send, not the document itself. Use it to check your request before you go live. Send every field printed on the ID, as in the example below.

```bash Request theme={null}
curl -X POST https://api.nuvion.dev/individual-entities \
  -H "Authorization: Bearer $NUVION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Ama Mensah",
    "person": {
      "first_name": "Ama",
      "last_name": "Mensah",
      "date_of_birth": "1990-04-12",
      "email": "ama.mensah@example.com",
      "nationality": "GH",
      "gender": "f",
      "phonenumber": "+233201234567"
    },
    "address": {
      "line_1": "12 Oxford Street",
      "city": "Accra",
      "state": "Greater Accra",
      "postal_code": "00233",
      "country_code": "GH"
    },
    "identification": {
      "document": {
        "type": "drivers_license",
        "number": "D1234567",
        "issue_date": "2022-03-01",
        "expiry_date": "2030-02-28",
        "issuing_country": "US",
        "issuing_authority": "California DMV"
      }
    }
  }'
```

```json identification in the 201 response theme={null}
{
  "id": "01HXYZ0040ABCDEFGHJKMNPQRS",
  "person_id": "01HXYZ0038ABCDEFGHJKMNPQRS",
  "verification_status": "pending",
  "created": 1793796790477,
  "updated": 1793796790477,
  "document": {
    "type": "drivers_license",
    "number": "***4567",
    "issue_date": "2022-03-01",
    "expiry_date": "2030-02-28",
    "issuing_country": "US",
    "issuing_authority": "California DMV",
    "verification_status": "pending"
  },
  "proof_of_address": {
    "verification_status": "pending"
  },
  "identity_numbers": []
}
```

The person's `nationality` is `GH` and the license comes from the US, so `issuing_country` is `US`. To test a failure, send a country name instead of the two-letter code, for example `"issuing_country": "Ghana"`. The API returns `422`:

```json 422 Unprocessable Entity theme={null}
{
  "status": "error",
  "message": "Validation failed for one or more fields",
  "type": "error_validation_error",
  "validations": [
    {
      "document.issuing_country": {
        "type": "error_validation_invalid_format",
        "message": "The 'document.issuing_country' format isn't valid. Please use the correct format."
      }
    }
  ]
}
```

## Before you go live

* Replace the base URL, API key and webhook URLs with production values.
* Create production webhooks. Sandbox webhooks do not carry over.
* Confirm your handlers retrieve the resource before acting on an event. See [Confirm state before acting](https://docs.nuvion.co/webhooks/overview.md#confirm-state-before-acting).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.