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

# Test mode

> Build against simulated providers with your test key — same base URL, same endpoints, isolated test wallet, live balances never touched.

Numero has **no separate sandbox host**. You test against the same base URL and the same endpoints — the key you send decides the mode:

| Key          | Mode     | Behaviour                                                          |
| ------------ | -------- | ------------------------------------------------------------------ |
| `live_key_…` | **Live** | Real money, real providers                                         |
| `test_key_…` | **Test** | Simulated providers, isolated test wallet, live balances untouched |

So the switch from testing to going live is: **change the key. Nothing else.**

## What test mode gives you

* **Simulated providers** — transfers, collections and card calls resolve against simulators, so nothing leaves your account.
* **An isolated test wallet** — separate from your live balance. Your live money is never read or moved in test mode.
* **Mode-aware webhooks** — test events are delivered only to webhook subscriptions registered as test, and carry `livemode: false`.

<Note>
  The sandbox endpoints below require a `test_key_`. Calling them with a live key returns a clear rejection — they can never touch live balances.
</Note>

## Test data

Test mode is **deterministic**: the input you send decides the outcome. No special headers, no dashboard toggles —
send one of the values below and you get that result, every time. For a simulated operation, **any input value not
listed here takes the happy path**, so the success case needs no magic values.

<Note>
  That applies to the *values* you send, not to every endpoint. An operation Numero does not yet simulate returns
  **HTTP 501** naming the gap, rather than a fabricated success — so a green test run means the call was actually
  exercised, not merely that nothing objected. If you hit a 501, tell us; it is a missing simulation, not your bug.
</Note>

Use these to build a test suite that covers your failure handling, not just the path where everything works.

### Verification & credit — the BVN decides

| BVN           | Outcome                                                                                    |
| ------------- | ------------------------------------------------------------------------------------------ |
| `22222222222` | **Verifies.** Full identity, and a healthy credit file (FICO **720**, rating `GOOD`)       |
| `11111111111` | **Thin file.** Known to the bureau, but never scored — every score field comes back `null` |
| `00000000000` | **Not found.** No record at the bureau                                                     |
| anything else | Verifies, same as `22222222222`                                                            |

`11111111111` is the one worth testing against. The fields are **present and `null`** — a bureau returning "we hold
no score for this person" is an answer, not an error, and your code has to survive it. See
[Reading `normalizedResult`](/api-reference/verification-overview#reading-normalizedresult).

**What you get back.** These are the real responses, not illustrations — `POST /business/verification/credit-score`:

| BVN           | `success` | `normalizedResult`                               | charged?                      |
| ------------- | --------- | ------------------------------------------------ | ----------------------------- |
| `22222222222` | `true`    | `score.ficoScore.score: "720"`, `rating: "GOOD"` | yes — see `walletAmount`      |
| `11111111111` | `true`    | keys present, `score.ficoScore.score: null`      | **yes** — the bureau answered |
| `00000000000` | `false`   | `null` — no fields, so no map                    | **no** — `walletAmount: null` |

Note the middle row: a thin file is a **successful, billable** verification whose answer happens to be "no score".
Branch on the field being `null`, not on `success`. Full bodies for all three are on the
[Credit](/api-reference/credit-overview#getting-a-numeric-score) page.

<Warning>
  **A `200` does not mean the verification passed.** The HTTP status describes the call; `data.success` and
  `data.statusCode` describe the *outcome*. A not-found is a perfectly successful API call reporting a negative
  result.
</Warning>

<Note>
  Test mode mirrors what each product really returns — including that **`credit-first-central` returns no score**.
  If you need a number to derive a lending band, use `credit-score` or `credit-summary`. We'd rather the sandbox
  tell you that now than have you discover it in production.
</Note>

### Transfers & payouts — the amount decides

| Amount                             | Outcome                                                     |
| ---------------------------------- | ----------------------------------------------------------- |
| ends in **`.99`** (e.g. `1000.99`) | **Fails** at the provider — insufficient funds              |
| ends in **`.55`** (e.g. `1000.55`) | **Pends**, then settles — use it to test the status webhook |
| anything else                      | Succeeds                                                    |

### Account validation (name enquiry)

| Account number              | Outcome                                           |
| --------------------------- | ------------------------------------------------- |
| `0000000000`                | Name enquiry **fails** — unresolvable beneficiary |
| `1234567890` (or any other) | Resolves to `JOHN DOE`                            |

### Bills & airtime

| Meter / phone / customer ID | Outcome                                        |
| --------------------------- | ---------------------------------------------- |
| `0000000000`                | The biller **rejects** it — customer not found |
| anything else               | Succeeds                                       |

<Warning>
  These values are **test mode only**. In live mode they are ordinary inputs with no special meaning — `1000.99` is
  just an amount. Never ship them to production.
</Warning>

## Fund your test wallet

Top up your test balance with simulated money.

```
POST /api/v1/business/sandbox/fund
```

| Field    | Type   | Required | Description                      |
| -------- | ------ | -------- | -------------------------------- |
| `amount` | number | Yes      | Simulated money to add, in naira |

```bash theme={null}
curl -X POST "https://api.usenumero.com/numeroaccount/api/v1/business/sandbox/fund" \
  -H "Content-Type: application/json" \
  -H "X-Numero-Api-Key: test_key_your_test_key" \
  -d '{ "amount": 500000 }'
```

Your test wallet starts at **₦1,000,000** and is capped at **₦10,000,000** — a top-up that would exceed the cap is rejected.

## Reset your sandbox

Wipe your test transactions and virtual accounts and reset the test balance to its default.

```
POST /api/v1/business/sandbox/reset
```

```bash theme={null}
curl -X POST "https://api.usenumero.com/numeroaccount/api/v1/business/sandbox/reset" \
  -H "X-Numero-Api-Key: test_key_your_test_key"
```

<Note>
  Test transactions are capped at **30** per merchant — the oldest are pruned automatically. Reset whenever you want a clean slate.
</Note>

## Fire a test webhook

Send a sample event to your registered **test** webhook endpoint(s) to exercise your handler without waiting for a real payment.

```
POST /api/v1/business/sandbox/webhooks/test-event
```

| Field       | Type   | Required | Description                                       |
| ----------- | ------ | -------- | ------------------------------------------------- |
| `eventType` | string | No       | Event to send. Defaults to `FUNDING_NOTIFICATION` |
| `amount`    | number | No       | Sample amount in the payload (naira)              |

```bash theme={null}
curl -X POST "https://api.usenumero.com/numeroaccount/api/v1/business/sandbox/webhooks/test-event" \
  -H "Content-Type: application/json" \
  -H "X-Numero-Api-Key: test_key_your_test_key" \
  -d '{ "eventType": "FUNDING_NOTIFICATION", "amount": 5000 }'
```

The delivery is signed exactly like a live webhook, so you can verify your signature check end-to-end — see [Webhook verification](/api-reference/webhooks-verification). It carries `livemode: false`.

## Going live

1. Build and verify everything with your `test_key_`.
2. Swap in your `live_key_` — same base URL, same endpoints.
3. Register your live webhook URL and confirm you're handling `livemode: true` deliveries.
