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

# Sandbox introduction

> Test your Stateless Verification API integration against stored fixtures without calling the real data source or spending credit.

Use sandbox mode with predetermined API payloads to retrieve every status this endpoint can return. Each payload is mapped to a specific outcome so you can confirm how your integration handles verified results.

This page covers how sandbox works, the matching rules, and the shared response envelopes. The copy-paste payloads for each verification live on their own pages in this section.

## How sandbox works

Sandbox is a property of your company account, not of the URL. When your account is in sandbox mode, the same production endpoints stop calling the real upstream source and answer from stored fixtures instead. Same URLs, same tokens, same authentication.

Sandbox differs from live in exactly two ways:

* The upstream source is never called. The response comes from a fixture.
* No credit is reserved or charged.

<Note>
  A sandbox response is deliberately identical in shape to a live one. There is no `sandbox` or `environment` field to branch on — that is the point, so code you write against sandbox works unchanged in production.
</Note>

## Going live

Nothing in your integration changes. Your account is switched to live mode and the same calls begin reaching the real sources. There is no URL or token to swap.

## Authentication

Use the same Bearer token you use in live:

```http theme={null}
Authorization: Bearer <your API token>
```

Sandbox mode is resolved from the authenticated company. See [Introduction to the Stateless Verification API](/stateless-verification-api/introduction#authentication) for how to create a token.

## Matching rules

Your request payload must match one of the documented sandbox requests exactly to get that scenario's response. A valid payload that matches nothing is answered as a no-match — HTTP `200` with `isValid` set to `false` — the same answer the real source gives for identity data it holds no record for.

Copy the payloads from the sandbox page for your verification to exercise the other outcomes.

| Rule         | Behaviour                                                              |
| ------------ | ---------------------------------------------------------------------- |
| Key order    | Does not matter.                                                       |
| Value case   | **Case-sensitive.** `CITIZEN` matches, `citizen` does not.             |
| Omitted keys | An omitted key and an explicit `null` are treated as the same thing.   |
| Numbers      | Numbers and numeric strings are equivalent — `1` and `"1"` both match. |
| `metadata`   | Ignored for matching and echoed back untouched. Send anything there.   |

<Warning>
  Testing with your own real details is expected to come back as a no-match. The sandbox has no records to check against, only the documented scenarios. This is not an error and does not mean your integration is wrong.
</Warning>

## Response placeholders

The sample responses on the sandbox pages use placeholders for values that change on every call:

| Field           | Meaning                                                                           |
| --------------- | --------------------------------------------------------------------------------- |
| `transactionId` | A fresh UUID is generated per request. The literal string shown is a placeholder. |
| `createdAt`     | The UTC timestamp of your request. The literal string shown is a placeholder.     |
| `metadata`      | Whatever you sent in the request, echoed back. `null` if you sent none.           |

## Sending metadata

`metadata` is ignored for fixture matching and echoed back untouched. Any scenario can be called with it:

```json theme={null}
{
  "familyName": "TAYLOR",
  "givenName": "JANE",
  "dob": "29-12-1990",
  "passportNumber": "PA1234567",
  "countryOfIssue": "AUS",
  "metadata": { "yourReference": "abc-123" }
}
```

## Error codes

| Code                    | HTTP  | Sandbox only | Meaning                                                                                                                                                                                                               |
| ----------------------- | ----- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SANDBOX_MISCONFIGURED` | `500` | Yes          | The sandbox fixture file is missing or unreadable. This is always an IDmeta-side fault, never your payload — an unrecognised payload is answered as a no-match. Contact support.                                      |
| `VALIDATION_FAILED`     | `400` | No           | The request body failed field validation before matching was attempted. The response carries an `errors` object and a `null` `transactionId`.                                                                         |
| `PLAN_NOT_AVAILABLE`    | `403` | No           | Your account does not have this verification type enabled. Enforced in sandbox exactly as in live.                                                                                                                    |
| `DVS_UNAVAILABLE`       | `500` | No           | The Australian DVS source could not be reached. The source-unavailable scenario on each Australia DVS sandbox page simulates this on demand.                                                                          |
| `PH_GOV_UNAVAILABLE`    | `500` | No           | A Philippine government source could not be reached. The source-unavailable scenario on [PhilSys Check Tier 2](/stateless-verification-api/sandbox/philsys-check-tier-2#source-unavailable) simulates this on demand. |
| `NOT_VERIFIED`          | `200` | No           | Informational default for a completed verification that did not match. A no-match is a successful call, not an error.                                                                                                 |

## Non-verification responses

Four responses do **not** use the verification envelope. They are produced before — or instead of — a verification result, so they carry no `verificationStatus`, `verificationStatusCode`, or `data`.

<AccordionGroup>
  <Accordion title="Validation failure — HTTP 400">
    Returned when the request body fails field validation. This happens **before** any fixture matching, so it is identical in sandbox and live. Note that `transactionId` is `null` and there is no `verificationStatus`.

    ```json theme={null}
    {
      "success": false,
      "code": "VALIDATION_FAILED",
      "message": "The request could not be validated. Please check the submitted fields.",
      "errors": {
        "familyName": ["The family name field is required."]
      },
      "transactionId": null,
      "verificationType": "australia_visa",
      "createdAt": "<UTC timestamp of the request>"
    }
    ```

    There is one exception. On [PhilSys Check Tier 2](/stateless-verification-api/sandbox/philsys-check-tier-2#validation-failures), a rejected face-liveness session fails validation *after* the request has reached the service, so that response carries a real `transactionId`, echoes `metadata`, and writes a transaction row with status `FAILED` (`6`).
  </Accordion>

  <Accordion title="Plan not enabled — HTTP 403">
    Returned when the authenticated company has no plan for the verification type. The plan gate is enforced in sandbox exactly as in live.

    This envelope is deliberately short — it carries no `code` field, despite `PLAN_NOT_AVAILABLE` appearing in the error-code list above.

    ```json theme={null}
    {
      "success": false,
      "message": "Forbidden",
      "createdAt": "<UTC timestamp of the request>"
    }
    ```
  </Accordion>

  <Accordion title="Insufficient credit — HTTP 402">
    Live only. Sandbox reserves no credit, so this is unreachable there. Note that the field is `error_code`, not `code`.

    ```json theme={null}
    {
      "success": false,
      "error_code": "INSUFFICIENT_CREDIT",
      "message": "Insufficient credit for plan australia_visa",
      "plan": "australia_visa",
      "createdAt": "<UTC timestamp of the request>"
    }
    ```
  </Accordion>

  <Accordion title="Unauthorized — HTTP 401">
    Returned when the `Authorization` header is missing, malformed, or carries a token that is not valid. Authentication is resolved before anything else, so this envelope is the shortest of all — no `code`, no `verificationType`, no `transactionId`.

    ```json theme={null}
    {
      "success": false,
      "message": "Unauthorized",
      "createdAt": "<UTC timestamp of the request>"
    }
    ```
  </Accordion>
</AccordionGroup>
