# Sandbox

> Free simulated MT4 and MT5 accounts. No card, no broker login, no terminal boot. Your first call in under two minutes.

Source: https://tickwire.io/docs/sandbox/

The sandbox gives you MT4 and MT5 accounts that answer the real API but run on a simulated
terminal. You don't need a card, a broker login or a subscription, and you don't wait for a
terminal to start. Everything you build against the sandbox works unchanged on a live account.

- **Free.** Sandbox usage is never billed and never counts as a seat.
- **Separate.** Sandbox keys start with `twsk_`. A sandbox key can't see or touch a live account,
  and a live key can't see or touch a sandbox one.
- **Marked.** Every data response from a sandbox account carries the header
  `X-Tickwire-Sandbox: true`, and sandbox accounts show `"sandbox": true`.
- **Limited.** Up to 5 accounts per sandbox, and 10 requests per second per account (bursts of
  20). Over the limit you get `429 rate_limited` with `Retry-After`. A sandbox also holds up to
  5 active sandbox keys and 20 each of API tokens, read-only keys and agent keys. Anonymous
  sign-up allows 5 sandboxes an hour and 10 a day from one address.
- **Kept while you use it.** A sandbox from anonymous sign-up is deleted, with its keys and
  accounts, after 7 days without an API call. Sandboxes you get from a live key or the dashboard
  stay as long as your account does.
- **Simulated state.** Balances, positions and orders live in memory. They reset when you
  redeploy the account, and may reset after a week of inactivity or a sandbox restart.

## Your first call in two minutes

```bash
export TW=https://api.tickwire.io

# 1. Get a sandbox key and a connected MT5 account (no sign-in needed)
curl -s -X POST "$TW/v1/sandbox/signup" -H "content-type: application/json" -d '{"platform": "mt5"}'
```

```json
{
  "sandbox": true,
  "tenantId": "9f3c…",
  "apiKey": "twsk_4b1e…",
  "account": { "id": "acc_7Qx", "platform": "mt5", "server": "Tickwire-Sandbox",
               "login": "1000", "state": "DEPLOYED", "connectionStatus": "CONNECTED" }
}
```

The key is shown once. Keep it:

```bash
export TW_KEY=twsk_4b1e…
export ACC=acc_7Qx

# 2. Read the account
curl -s "$TW/users/current/accounts/$ACC/account-information" -H "auth-token: $TW_KEY"

# 3. Buy 0.01 EURUSD
curl -s -X POST "$TW/users/current/accounts/$ACC/trade" \
  -H "auth-token: $TW_KEY" -H "Idempotency-Key: first-order-1" \
  -H "content-type: application/json" \
  -d '{"actionType": "ORDER_TYPE_BUY", "symbol": "EURUSD", "volume": 0.01}'

# 4. See the position
curl -s "$TW/users/current/accounts/$ACC/positions" -H "auth-token: $TW_KEY"
```

The trade answers `"outcome": "applied"` with `numericCode` 10009. Send step 3 again with the
same `Idempotency-Key` and you get the same answer and no second position.

The sandbox quotes `EURUSD`, `GBPUSD`, `USDJPY` and `XAUUSD`. Prices move a little every second.

A key from anonymous sign-up is limited to your sandbox accounts, trading, streaming and reads.
It can't create webhooks, pre-trade hooks, agent keys or more keys, and it can't create, change
or delete risk-management trackers or guard rules (it can still read trackers, statistics and
metrics). For those, sign in and get a sandbox key from your live key; it has full rights inside
your sandbox:

```bash
curl -s -X POST "$TW/v1/sandbox/keys" -H "X-API-Key: $YOUR_LIVE_KEY"
```

## Add more sandbox accounts

Use the ordinary account API with your `twsk_` key, server `Tickwire-Sandbox`, and a **magic
login** from the table below. Any password works.

```bash
curl -s -X POST "$TW/users/current/accounts" \
  -H "auth-token: $TW_KEY" -H "content-type: application/json" \
  -d '{"name": "lost reply", "platform": "mt5", "server": "Tickwire-Sandbox",
       "login": "1031", "password": "anything"}'
```

The account is deployed at once. Its `connectionStatus` comes from the simulated terminal's own
health check, the same rule as a live account: `CONNECTED` only when that check says so.

## Magic logins

Like test card numbers, each login makes the sandbox simulate one condition, every time. They
work on `mt5` and `mt4`. Any other login behaves like `1000`.

| Login | What happens | What you should see |
|---|---|---|
| `1000` | Normal account | Connects, quotes move, orders fill: `outcome` `applied` |
| `1031` | Lost reply ending **applied**: the reply to every order is lost, but the broker executed it | `outcome` `applied`, `numericCode` 10009. The terminal reported no connection, but Tickwire found the fill, so there's nothing to retry |
| `1032` | Lost reply ending **unknown**: the reply to new orders is lost and no fill appears | After about 3 s, `outcome` `unknown`, `numericCode` -11. Check positions before you retry. Closes and modifies work normally |
| `1019` | Rejection: no money | `outcome` `rejected`, `numericCode` 10019. Nothing happened |
| `1006` | Rejection: request rejected | `outcome` `rejected`, `numericCode` 10006 |
| `1040` | Disconnect and reconnect | Connected for 60 s, then the broker link drops for 30 s, then it comes back; repeats every 90 s. While it's down, reads answer `503` and trades answer `numericCode` -10 (nothing sent); health says `disconnected`, then `healthy` again |
| `1050` | Stale quotes | Connected, but prices never move. After 30 s health says `stale` |
| `1060` | OTP required | The account never connects: it ends `DEPLOY_FAILED` with error `ERR_OTP_REQUIRED`. Its health answers `200` with `status` `down` and `reason_code` `ERR_OTP_REQUIRED` (see [Health](https://tickwire.io/docs/health/)) |
| `1061` | Login required: wrong login or password | Ends `DEPLOY_FAILED` with error `E_AUTH`. Health is `down` with `reason_code` `E_AUTH` |
| `1080` | Slow fills | Every order takes 3 s, then fills |

MT4 accounts (`"platform": "mt4"`) behave as MT4 does. Market depth
(`…/symbols/{symbol}/current-book`) and tick history (`…/historical-market-data/…/ticks`) answer
with the error `not_supported_on_mt4`. Try it with any login.

## Moving to live

1. Create a live key on the dashboard, and connect a broker account (see the
   [Quickstart](https://tickwire.io/docs/quickstart/)).
2. Swap `twsk_…` for the live key and `Tickwire-Sandbox` for your broker's server.

Nothing else changes: same paths, same fields, same outcomes. Sandbox accounts stay in the
sandbox; they're never moved to or mixed with live ones.

## Errors you may see

| Status | `error` / `code` | Meaning |
|---|---|---|
| 400 | `sandbox_key_required` | You used server `Tickwire-Sandbox` with a live key |
| 400 | `sandbox_server_required` | You used a sandbox key with a real broker server |
| 403 | `sandbox_account_limit` | Your sandbox already has 5 accounts; delete one |
| 403 | `sandbox_key_limit` | Your sandbox already has 5 active keys (or 20 API tokens, read-only keys or agent keys); revoke one on the dashboard |
| 403 | `sandbox_key_restricted` | A sign-up key tried something other than accounts, trading or reads (for example a risk-management write) |
| 429 | `rate_limited` | Too many requests, or too many sign-ups from your address. Wait for `Retry-After` |
