# Test mode

> A mock provider and a test wallet behind the same API, with every outcome on demand.

Source: https://useroutegate.com/docs/concepts/test-mode

A key that starts with `rg_test_` works in test mode. It calls the same endpoints and gets the same envelope, statuses and webhooks as a live key, but:

- Vends go to a mock provider and nothing else. No line, gateway or real carrier is ever called, and there is no failover: the mock is the only route.
- Money comes from the business's test wallet, which is separate from the live one and starts empty.
- Transactions, webhook endpoints and idempotency keys are kept apart from live ones. Every transaction and event says `"mode": "test"`.

## Before the first test vend

Every business has the mock provider in test mode from the start; it needs no credentials and nothing to enable. It never runs in live mode. One thing to do in the dashboard, with the mode switch on **Test**:

1. **Top up the test wallet.** Open **Wallet** and use **Top up the test wallet**. The credit is immediate and sends `wallet.funded`. A vend the test wallet cannot cover is refused with `402 WALLET_INSUFFICIENT_FUNDS`.

## Choosing the outcome

The mock decides by the last four digits of the destination: the phone number for airtime and data, the meter or smartcard number for bills.

| Ends in       | What happens                                                                                         | Webhooks                                             |
| ------------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| `0000`        | The provider refuses it: `failed`, `failure_code` `PROVIDER_REJECTED`.                               | `transaction.failed`                                 |
| `1111`        | A temporary error. Test mode has no other route to fail over to, so it ends `failed`.                | `transaction.failed`                                 |
| `2222`        | No answer: `pending`, then `failed` at the first status query.                                       | `transaction.pending`, then `transaction.failed`     |
| `3333`        | The provider accepts it without confirming: `pending`, then `successful` at the second status query. | `transaction.pending`, then `transaction.successful` |
| `4444`        | `pending`, then `failed` at the first status query.                                                  | `transaction.pending`, then `transaction.failed`     |
| Anything else | `successful`.                                                                                        | `transaction.successful`                             |

The first status query runs about 10 seconds after a transaction goes `pending`, and each one after waits twice as long as the last. A `failed` vend releases its hold, so the test wallet gets the money back.

Five temporary errors in a row can open the mock's circuit breaker for 30 seconds, as they would a real provider's. Every test vend in that window fails, whatever its destination.

```bash
# Ends in 3333: pending first, successful a little later.
curl -X POST https://api.useroutegate.com/api/v1/airtime \
  -H "Authorization: Bearer $ROUTEGATE_API_KEY" \
  -H "Idempotency-Key: ord-00046" \
  -H "Content-Type: application/json" \
  -d '{"network": "mtn", "destination": "+2348031233333", "amount_kobo": 10000, "client_reference": "ord-00046"}'
```

A successful test electricity vend puts a sample token, units and receipt number in the transaction's `metadata`.

## Going live

Create a key with mode **Live** and send the same requests with it. Live vends are sold from the MTN lines your business has connected under **Lines**, and are paid from the live wallet, which you [fund](/docs/concepts/wallet) separately. Register a live webhook endpoint too: test endpoints never receive live events, and the new endpoint has its own secret.
