> ## Documentation Index
> Fetch the complete documentation index at: https://public-openfx.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Environments

> The Sandbox and Live environments available on the OpenFX API, and how a request selects between them.

OpenFX runs Sandbox and Live on the **same base URL** (`api.openfx.com`). Which environment you hit is determined by the **API key** you authenticate with. Each key is bound to one environment server-side. A request can also carry an `X-App-Mode` header (<span class="ofx-enum">SANDBOX</span> or <span class="ofx-enum">LIVE</span>) matching that key's environment. Omit it and the request defaults to <span class="ofx-enum">SANDBOX</span>, so a Live key must send `X-App-Mode: LIVE` explicitly or the request fails with `403` <span class="ofx-enum">AUTH\_API\_KEY\_SANDBOX\_REQUIRED</span>. Any other value returns `400` <span class="ofx-enum">INVALID\_APP\_MODE</span>. Sandbox API keys carry a **`sandbox_` prefix**; Live keys are unprefixed, so you can tell the two apart at a glance. Sandbox and Live use the same endpoints, code paths, and rate-limit ceiling; the difference is real-vs-simulated funds and the key prefix. Code validated in Sandbox therefore follows the same API contract, traffic pattern, and rate limits in Live.

## Sandbox vs Live

| Aspect | Live | Sandbox |
| - | - | - |
| Base URL | `api.openfx.com` | `api.openfx.com` (same) |
| API key | Unprefixed (bound to env server-side) | `sandbox_`-prefixed (bound to env server-side) |
| `X-App-Mode` header | <span class="ofx-enum">LIVE</span> | <span class="ofx-enum">SANDBOX</span> |
| Real funds | Yes | No (simulated) |
| Webhook signing secret | `whsec_…` | `sandbox_whsec_…` |
| Rate limits | 300 requests / 10-second window | 300 requests / 10-second window (same as Live) |
| Data persistence | Permanent | May reset periodically |

Sandbox and Live share the same rate-limit ceiling as separate buckets, so a traffic pattern validated in Sandbox carries over directly. See [Rate limiting](/v3/rate-limiting) for the back-off pattern.

## Sample request

A Sandbox call differs from a Live call by the `sandbox_`-prefixed key and the `X-App-Mode` value; everything else, including the required `X-Request-Signature`, is the same.

<Warning>
  **Use the interactive API reference playground with Sandbox credentials
  only.** Never paste a Live key or Live `X-App-Mode` value into a browser-based
  tool. Use a language sample with your own request-signing code for Live calls
  instead.
</Warning>

```bash theme={"theme":{"light":"poimandres","dark":"poimandres"}}
# X-Request-Signature is an ES256 signature over the canonical request string;
# compute it with one of the language helpers in /v3/authentication (shell signing is impractical).

# Sandbox
curl https://api.openfx.com/v3/fx/pairs \
  -H "Authorization: Bearer $OPENFX_SANDBOX_JWT" \
  -H "X-Request-Signature: $OPENFX_SIGNATURE" \
  -H "X-App-Mode: SANDBOX"

# Live
curl https://api.openfx.com/v3/fx/pairs \
  -H "Authorization: Bearer $OPENFX_LIVE_JWT" \
  -H "X-Request-Signature: $OPENFX_SIGNATURE" \
  -H "X-App-Mode: LIVE"
```

The JWT in each case was minted with the matching environment's API key (Sandbox keys carry a `sandbox_` prefix; Live keys don't). See [Authentication](/v3/authentication) for the minting flow.

## Safety net: wrong-environment errors

If your key doesn't match the environment you're trying to reach, OpenFX rejects the request instead of silently doing the wrong thing: you can't accidentally execute a Live trade with Sandbox credentials, or vice versa.

| Error code | When you'll see it |
| - | - |
| <span class="ofx-enum">AUTH\_API\_KEY\_SANDBOX\_REQUIRED</span> | A Live key reaches Sandbox, including a Live key that omits `X-App-Mode` |
| <span class="ofx-enum">AUTH\_API\_KEY\_LIVE\_REQUIRED</span> | Live traffic uses a Sandbox key |

Both codes return <span class="ofx-enum">AUTHORIZATION\_ERROR</span> / 403: the request authenticated successfully, but the key isn't valid for this environment. An `X-App-Mode` value other than <span class="ofx-enum">LIVE</span> or <span class="ofx-enum">SANDBOX</span> returns `400` <span class="ofx-enum">INVALID\_APP\_MODE</span>, and `details.allowed` lists the accepted values. Full entries live in the v3 [error catalog](/v3/errors#error-code-catalog).

## Going live

The full pre-launch list lives in the [Live checklist](/v3/live-checklist). The minimum cutover from a working Sandbox integration is:

<Steps>
  <Step title="Swap credentials">
    Replace the Sandbox `name` and `privateKey` in your secrets store with the
    Live values from the dashboard.
  </Step>

  <Step title="Set X-App-Mode to LIVE">
    Send `X-App-Mode: LIVE` on every request. An omitted header defaults to
    <span class="ofx-enum">SANDBOX</span>, and a Live key then returns `403`
    <span class="ofx-enum">AUTH\_API\_KEY\_SANDBOX\_REQUIRED</span>. Wire the value
    from the same config that selects your key.
  </Step>

  <Step title="Update webhook signing key">
    Live webhook signing secrets are prefixed `whsec_` (Sandbox secrets are
    `sandbox_whsec_…`). Point your verifier at the Live signing secret.
  </Step>
</Steps>

<Tip>
  Run end-to-end through Sandbox first. Identical endpoints, code paths, and
  rate limits mean what works in Sandbox works in Live, including load patterns
  tested against the 300 req/10s ceiling.
</Tip>

## Common mistakes

* **Mixing credentials in the same process.** Two key pairs (one Sandbox, one Live) stored in two distinctly-named secrets. Never read `OPENFX_KEY` and hope it's the right one.
* **Assuming Sandbox data persists.** Sandbox may be reset periodically; don't seed long-running test workflows with Sandbox IDs you expect to still exist next month.
* **Hard-coding the base URL.** It's the same in both environments, but writing `api.openfx.com` as a literal in two places will trip you up the day OpenFX adds a regional endpoint.
* **Sending an `X-App-Mode` value that doesn't match the key.** The header and the key's bound environment must agree. Wire it from the same environment-aware config that selects your key, not a hard-coded constant.

## What's next

<CardGroup cols={2}>
  <Card title="Live checklist" icon="https://public-assets.openfx.com/docs/icons/live-checklist.svg" href="/v3/live-checklist">
    Full pre-launch checklist: keys, IP allowlist, monitoring.
  </Card>

  <Card title="Authentication" icon="https://public-assets.openfx.com/docs/icons/authentication.svg" href="/v3/authentication">
    JWT minting flow with code samples in Node, Python, Java, C++, and Go.
  </Card>

  <Card title="Errors" icon="https://public-assets.openfx.com/docs/icons/errors.svg" href="/v3/errors#authentication-authorization">
    `AUTH_*` codes and recovery patterns.
  </Card>
</CardGroup>
