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

# Sandbox

> Magic numbers, simulated outcomes, and balance testing for integration development

Use sandbox API keys to test your integration end to end. Any valid E.164 destination works, and magic numbers force specific outcomes for deterministic failure-path testing. No real faxes are sent, and no real credits are spent.

## Prerequisites

* A mintfax account. A sandbox environment is created automatically when you register.
* A sandbox API key (prefixed `mfx_test_`). Create additional keys from the dashboard or via the API.

## How the sandbox works

Sandbox uses the same API as live. Three things differ: no real charges, no real recipients, no activation gate. See [Sandbox and live](/environments#sandbox-and-live) for the full comparison.

### Environments and keys

Sandbox environments have their own API keys, fax records, and webhook endpoints, all isolated from live. Activity in the sandbox never touches your live environments. The reverse is also true.

Sandbox API keys start with `mfx_test_`. Live keys start with `mfx_live_`. The prefix tells mintfax which environment to use. Nothing else to configure.

### Credits

Credits pool at the account level. Your account has one sandbox balance and one live balance, both shared across every environment of that type under the account. If you create multiple sandbox environments, they all draw from the same virtual sandbox pool. See [Credits](/credits) for the full account-pool model.

When you send a sandbox fax, mintfax places a hold on the account's sandbox balance, captures it on delivery, and releases it on failure. The mechanics are identical to live.

### Multiple sandboxes

You can create as many sandbox environments as you need. Common patterns: one per engineer for parallel development, a dedicated one for CI, or separate environments for staging and feature-branch testing. All sandboxes under the same account share the same virtual credit pool.

## Destinations

The sandbox accepts any valid E.164 phone number. Numbers that aren't magic numbers default to a successful delivery on the first attempt, so you can send realistic-looking test traffic without picking from a specific range.

## Magic numbers

Magic numbers are reserved destinations that force a specific outcome on every call. Use them in integration tests to deterministically exercise failure paths without depending on carrier behavior.

| Number         | Scenario             | Behavior                                         |
| -------------- | -------------------- | ------------------------------------------------ |
| `+15005550001` | Success              | Always succeeds on first attempt                 |
| `+15005550002` | Busy                 | Returns busy on every attempt                    |
| `+15005550003` | No answer            | Rings out without pickup                         |
| `+15005550004` | Transient failure    | Fails first attempt, succeeds on retry           |
| `+15005550005` | Permanent failure    | Always fails permanently                         |
| `+15005550006` | Insufficient balance | Returns insufficient\_balance error before send  |
| `+15005550099` | Validation failure   | Returns validation error (invalid number format) |

## Step 1: Send a fax to the success number

Submit a fax to `+15005550001` with your sandbox API key.

```bash theme={null}
curl -X POST https://api.mintfax.com/v1/faxes \
  -H "Authorization: Bearer mfx_test_abc123def456" \
  -F "to=+15005550001" \
  -F "file=@document.pdf"
```

The response includes a fax ID and a `queued` status. mintfax places a credit hold on your sandbox balance at this point.

```json theme={null}
{
  "id": "fax_01H7N9WXYZ8VC2QPK5MTRDE3FA",
  "status": "queued",
  "to": "+15005550001"
}
```

**Verify:** poll `GET /v1/faxes/{id}` until `status` reaches `delivered`. The hold is captured and your sandbox balance decreases by the per-page cost.

## Step 2: Simulate a failure

Send a fax to `+15005550002` to simulate a busy line.

```bash theme={null}
curl -X POST https://api.mintfax.com/v1/faxes \
  -H "Authorization: Bearer mfx_test_abc123def456" \
  -F "to=+15005550002" \
  -F "file=@document.pdf"
```

Poll the fax status:

```bash theme={null}
curl https://api.mintfax.com/v1/faxes/fax_01H7N9WXYZ8VC2QPK5MTRDE3FA \
  -H "Authorization: Bearer mfx_test_abc123def456"
```

```json theme={null}
{
  "id": "fax_01H7N9WXYZ8VC2QPK5MTRDE3FA",
  "status": "failed",
  "to": "+15005550002",
  "error": "fax_busy",
  "message": "Destination busy"
}
```

The credit hold is released back to your sandbox balance on failure.

**Verify:** call `GET /v1/environment/balance` with your sandbox key and confirm the balance returned to its previous value. (To read both your live and sandbox balances together, call `GET /v1/account/balance` with your `mfx_acct_` key instead. A sandbox key used against the account route returns [`key_not_scoped_for_account`](/errors#key-not-scoped-for-account).)

## Step 3: Test retry behavior

`+15005550004` fails on the first attempt, then succeeds on retry. Use it to confirm your integration handles transient failures.

```bash theme={null}
curl -X POST https://api.mintfax.com/v1/faxes \
  -H "Authorization: Bearer mfx_test_abc123def456" \
  -F "to=+15005550004" \
  -F "file=@document.pdf"
```

If you have a webhook endpoint registered, you will receive a `fax.sending` event for each attempt (one for the failed first try, one for the successful retry), then a `fax.delivered` once the retry succeeds. This is the same event sequence a transient carrier failure produces in live.

**Verify:** the final fax status should be `delivered`.

## Step 4: Test insufficient balance

Send to `+15005550006` to trigger an `insufficient_balance` error. This number rejects the fax before submission regardless of your actual sandbox balance, so you can test the error path without draining credits first.

```bash theme={null}
curl -X POST https://api.mintfax.com/v1/faxes \
  -H "Authorization: Bearer mfx_test_abc123def456" \
  -F "to=+15005550006" \
  -F "file=@document.pdf"
```

```json theme={null}
{
  "error": "insufficient_balance",
  "message": "Insufficient balance to submit fax",
  "action": "top_up_balance",
  "docs": "/docs/errors#insufficient-balance"
}
```

The response is HTTP 402. Your integration should handle this by prompting a balance top-up or alerting the operator. See the [error catalog](/errors) for the full list of error codes and recommended actions.

**Verify:** no fax record was created and your balance is unchanged.

## Step 5: Test validation errors

Send to `+15005550099` to receive a validation error. This is what happens when a fax number fails format validation.

```bash theme={null}
curl -X POST https://api.mintfax.com/v1/faxes \
  -H "Authorization: Bearer mfx_test_abc123def456" \
  -F "to=+15005550099" \
  -F "file=@document.pdf"
```

The response is HTTP 422 with field-level error details.

**Verify:** confirm your integration surfaces the validation error to the caller.

## Sandbox balance

Your account's virtual sandbox pool is seeded on registration from a designated `CreditPackage` row, so you can start sending sandbox faxes immediately. The seed lives at the account level, not per-environment. If you spin up additional sandbox environments under the same account, they all share this same sandbox pool.

Credits are consumed the same way as in live: hold on submit, capture on delivery, release on failure. No real money is involved.

You can also trigger the `insufficient_balance` error organically by exhausting your sandbox balance with enough faxes, then submitting one more. Top up sandbox credits from the dashboard or via the API.

Auto-top-up is configurable per balance row in the dashboard. If you have it enabled on your sandbox balance, it fires at the configured threshold the same way it does in live.

See [Credits](/credits) for the full account-pool model, and [the deliberate asymmetry](/credits#the-deliberate-asymmetry) for why balance and transaction reads split between the account and environment scopes.

## What the sandbox does not do

In practice:

* Your payment method is never charged for sandbox activity, including auto-top-up.
* Sandbox faxes never reach a real fax machine.
* Sandbox API keys (`mfx_test_`) cannot authenticate against a live environment, and vice versa. The prefix is enforced at every ingress.

One residual divergence: `GET /v1/faxes/{id}/image` returns 404 for sandbox faxes today. The sandbox exercises status transitions, webhooks, and balance behavior, but does not currently render fax images.

## Writing integration tests

Magic numbers are deterministic. You can write automated tests against them without mocks or carrier dependencies.

A minimal test suite covers four paths:

1. **Happy path.** Send to `+15005550001`, poll until `delivered`, assert the balance decreased.
2. **Failure handling.** Send to `+15005550005`, poll until `failed`, assert the hold was released.
3. **Retry recovery.** Send to `+15005550004`, poll until `delivered`, assert the fax recovered after a transient failure.
4. **Balance guard.** Send to `+15005550006`, assert HTTP 402 and the `insufficient_balance` error code.

Each test runs against the sandbox with your `mfx_test_` key. No cost, no external dependencies.

## Verify

After working through the steps above, you have exercised success, busy failure, transient retry, insufficient balance, and validation error paths. That covers the outcomes most integrations need.

## What to do next

* [Error catalog](/errors) for the full list of error codes, HTTP statuses, and recommended actions.
* [Glossary](/glossary) for definitions of sandbox, environment, E.164, and other terms used on this page.
