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

# Sandbox and production

> Two environments, one hostname, and a key that decides which you are in.

Hypermid has two environments. They share **one hostname** —
`https://server.hypermid.io` — and are told apart entirely by the key you send.

|            | Sandbox                      | Production                |
| ---------- | ---------------------------- | ------------------------- |
| Secret key | `sk_test_…`                  | `sk_live_…`               |
| Chains     | Testnets only                | Mainnets only             |
| Money      | None. Test tokens.           | Real.                     |
| Available  | As soon as you are onboarded | After production approval |

<Warning>
  There is no `sandbox.hypermid.io`. If you find yourself building a base-URL
  switch, you have the wrong model — swap the **key**, not the host.
</Warning>

## Why the key and not the URL

A separate hostname makes the environment a property of your *configuration*,
and configuration is exactly what gets copied wrongly between deploys. The
usual failure is a staging box pointed at a production URL with a key that
happens to work.

Binding the environment to the key removes that class of mistake. The
confinement is enforced server-side on the key itself, so:

* A `sk_test_` key asking for a **mainnet** chain is rejected. It cannot move
  real money regardless of what it requests.
* A `sk_live_` key asking for a **testnet** chain is rejected too, which
  catches the reverse mistake — a test config that quietly reached production.

The error says which direction you hit, because the two need opposite fixes:

```json theme={"system"}
{
  "code": "UNSUPPORTED_CHAIN",
  "error": "toChain: 8453 is a mainnet chain and this is a TEST key"
}
```

## What sandbox actually runs on

Three real public testnets. Nothing is simulated — these are genuine chains
with genuine block times, so settlement takes as long as it really takes.

<CardGroup cols={3}>
  <Card title="Base Sepolia" icon="link">
    Chain id `84532`. Same-chain swaps and cross-chain both available.
  </Card>

  <Card title="Sepolia" icon="link">
    Chain id `11155111`. Same-chain swaps and cross-chain both available.
  </Card>

  <Card title="BNB Smart Chain Testnet" icon="link">
    Chain id `97`. Cross-chain enabled; no same-chain swap router.
  </Card>
</CardGroup>

You will need testnet gas and testnet tokens from the usual public faucets;
we do not mint them for you.

<Note>
  The catalog endpoints are environment-aware. Called with a sandbox key,
  [`/v1/chains`](/api-reference) and `/v1/tokens` return **only** these three
  chains and their tokens — even if you explicitly ask for mainnet. So a picker
  built against the catalog is automatically correct in both environments with
  no branching on your side.
</Note>

## Fiat in sandbox

Card and bank payments work in sandbox, but they need **separate onramp
credentials** from your production ones — they are different tenants at the
provider, with different merchant accounts behind them.

Until sandbox onramp credentials are configured on your account,
`fiatEnabled` comes back `false` on every session and the fiat option simply
does not appear. That is the intended signal, not an error. Ask
[support@hypermid.io](mailto:support@hypermid.io) to set them up.

## Moving to production

<Steps>
  <Step title="Build and test entirely on sandbox">
    Every product works there. There is nothing you can only learn in
    production except real liquidity.
  </Step>

  <Step title="Ask for production access">
    Email [support@hypermid.io](mailto:support@hypermid.io). Live keys are
    issued only once your account is approved.
  </Step>

  <Step title="Swap the key">
    Change `sk_test_…` to `sk_live_…` in your server config. That is the entire
    migration — the host, the paths, the payloads and the webhook format are
    identical.
  </Step>
</Steps>

## Configuration is per environment

Everything your key resolves to is scoped the same way the key is. Register a
webhook endpoint with a `sk_test_…` key and it belongs to sandbox: it receives
sandbox completions only, and a live key can neither see it nor read its
signing secret. The reverse holds too.

That makes the endpoint list depend on which environment you configure: sandbox
endpoints receive sandbox completions only, and production endpoints receive
production completions only.

<Note>
  A new sandbox starts with **no** endpoints even if production has several.
  Configuration never crosses between environments, so there is nothing to
  inherit — register the sandbox one explicitly.
</Note>

Fee overrides work the same way: your sandbox Hypermid fee and developer fee are
set independently of production. A partner Hypermid override replaces the
global rate outright; the developer fee defaults to 0, is limited to 0–100
basis points, and is added on the same base amount.

The **checkout payout allowlist** is scoped too. A sandbox checkout may only pay
a recipient allowlisted for sandbox, so testing on a testnet no longer means
adding a testnet address to the list production pays out to.

<Warning>
  The allowlist fails **closed**, per environment. A partner allowlisted before
  this existed has those entries as LIVE, and an empty sandbox list — so the
  first sandbox checkout is refused with `RECIPIENT_NOT_ALLOWLISTED` until a
  sandbox recipient is added. That is the intended direction for a control whose
  job is to stop funds reaching an address you did not name.
</Warning>

Add a sandbox recipient through **Payout addresses** in the partner portal.
The portal sends an emailed verification code for the exact change; enter it
before the recipient is added. Select sandbox before starting: the same
control manages a separate production allowlist.

<Note>
  The environment comes from the key, never from the address or the chain. A
  live key adding a testnet address still writes a **production** entry — which
  will not satisfy a sandbox checkout, and is not what you want on the list your
  real payouts are checked against. Use the key for the environment you are
  configuring.
</Note>

<Warning>
  Still branch on the session you receive before crediting anything. Scoped
  endpoints mean a sandbox completion cannot reach your production URL, but a
  handler that serves both environments must tell them apart itself — a sandbox
  completion and a real one are the same shape.
</Warning>
