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

> ## Agent Instructions
> HEVN accounts are non-custodial in both products. Self-serve (the General tab): the person who owns the email holds the only key and signs for themselves. Whitelabel (the Whitelabel and REST API tabs): the integrator's customer creates and owns their account through Sign in with HEVN, a HEVN-hosted popup on the integrator's site; the integrator's backend holds a developer key, operates linked accounts and books their payouts, and the account holder approves every payout in that popup. The developer key signs only for the integrator's own wallet; it never signs a client's money.
> Use HEVN's terms exactly. A rail is a bank route a client can request. A virtual account (bnk_) is an opened rail. Account details are what a payer needs in order to wire money, and arrive in a field named requisites. A client is a customer's own account linked to the integrator through Sign in with HEVN (POST /dapi/v1/connect/sessions, then POST /dapi/v1/clients/link); there is no API call that creates one. The business behind it is the integrator's customer. A contact is a saved destination; a beneficiary is the party at the far end of a fiat payout. Every departure of money is a payout, to a bank account or to a wallet alike.
> Never name a banking partner. Say 'a licensed banking partner'.
> There are no published fee rates, spreads or minimums. Prices come from a quote on a specific payment; direct pricing questions to HEVN rather than inventing numbers.
> The whitelabel API is at /dapi/v1 and takes a developer-key session plus an X-Hevn-Account header. Developer keys have an IP allowlist and no scopes. Remote transfers are at /api/v1/remote_spend_limit and take a remote transfer key (rsl_) in X-Api-Key, bounded by an onchain spend permission the account holder grants. They are different credentials and neither works on the other surface. The HEVN CLI is discontinued.

# Going live

> What HEVN must enable, what you re-create per environment, what stops being instant, and what production starts refusing.

Production differs in four ways: what HEVN must enable for you, what you re-create per environment, what stops being instant, and what starts being refused. The routes, bodies and error slugs are identical.

```mermaid theme={null}
flowchart TB
    subgraph Sandbox
        SA["sandbox.gethevn.com<br/>developer key, sign-in window"]
        SB["sandbox-api.hevn.finance/dapi/v1"]
        SC["Base Sepolia · one emulated partner<br/>instant rails · instant KYB · /dapi/v1/sandbox/*"]
        SA --- SB --- SC
    end
    subgraph Production
        PA["app.gethevn.com<br/>developer key, sign-in window"]
        PB["api.hevn.finance/dapi/v1"]
        PC["Base · real partners<br/>reviewed rails · reviewed KYB · no /dapi/v1/sandbox/*"]
        PA --- PB --- PC
    end
    Sandbox -. "nothing crosses: developer keys and their allowlists,<br/>sign-in window settings, clients, tokens, contacts, escrow deals" .-> Production
```

| Environment | API base URL | App, in a browser | Chain |
| - | - | - | - |
| Sandbox | `https://sandbox-api.hevn.finance/dapi/v1` | `https://sandbox.gethevn.com` | Base Sepolia |
| Production | `https://api.hevn.finance/dapi/v1` | `https://app.gethevn.com` | Base |

Every example on these pages reads the same variables:

```bash theme={null}
export HEVN_API="https://sandbox-api.hevn.finance/dapi/v1"
export HEVN_EMAIL="integrator@example.com"
export HEVN_KEY_PEM="$HOME/.hevn/developer-key.pem"  # mode 0600, never in your repo
export HEVN_CLIENT_ID="cl_7YQ2Kf3mN8"                # the client a call acts for
```

Three more come out of the first two: `HEVN_ACCESS_TOKEN`, the one-hour token
[logging in](https://docs.gethevn.com/whitelabel/sessions.md) returns; `HEVN_USER_ID`, your own account's `cl_…` id, which the
same response returns as `userId` and which the `/client*` routes need in `X-Hevn-Account` when you
read your own account; and `HEVN_PUBLIC_KEY`, the base64 public half of the PEM, which
`POST /dapi/v1/payouts` takes as `publicKey`.

```bash theme={null}
export HEVN_PUBLIC_KEY=$(openssl ec -in "$HEVN_KEY_PEM" -pubout -outform DER 2>/dev/null \
  | base64 | tr -d '\n')
```

Nothing crosses between the two environments: developer keys, clients and tokens are
per-environment, and a sandbox token is refused in production.

## What HEVN must enable

Three things are granted by HEVN rather than by the API — in the sandbox exactly as in production — and each one has a refusal that tells you it is missing.

| What | Without it | Ask for it when |
| - | - | - |
| An active integrator profile that may link clients | `403 client_creation_not_enabled` on `POST /dapi/v1/connect/sessions` and `POST /dapi/v1/clients/link`, and `403 account_forbidden` on every call that acts for a client you already have | Before you write production code. It follows a partnership review, off-platform. |
| Escrow operation | Escrow actions are refused | Only if funds must be locked on chain between your own customers. |
| The rails you need | `403 rail_not_available` on `POST /dapi/v1/banks` | Name the currencies and countries you sell into. Rail availability is per integrator, per client and per partner. |

Confirm all three the same way you would in the sandbox: link one client, read `GET /dapi/v1/banks` for it, and check that the rails you expect are listed.

## Nothing carries over

| Re-create in production | Why |
| - | - |
| The developer key | Keys belong to one environment. Create a fresh one in the production app, with the **production** egress IPs in its allowlist. The sandbox key, and the sandbox allowlist, are both unknown in production. |
| The sign-in window | **Allowed sites** and the window's look are per environment. Add your production origins in the production app, and ask HEVN to brand the production window; a sandbox origin there answers `403 redirect_uri_not_allowed`. |
| Every client | A `cl_…` from the sandbox does not exist in production, and neither does anything hanging off it: KYB, rails, contacts, escrow deals. Your customers link again, in production. |
| Tokens | Access and refresh tokens are environment-scoped. |
| Virtual accounts | Product codes (`eu`, `us`, …) are the same everywhere, but which products a client can request and the rail behind each one differ. Read `GET /dapi/v1/banks` per client, per environment, and never store the `rail` string. |
| Idempotency keys | Keys are scoped to your account and the environment. A key that replayed a sandbox payout means nothing in production. |

Keep both sets of credentials loadable at once and choose by `HEVN_API`: a deploy that points a production key at the sandbox host fails at login with `401 invalid_credentials`, and nothing in that refusal says you swapped a variable.

The allowlist is the second variable that fails this way. A key is pinned to the source IPs you gave it and cannot be edited, so a production deploy behind a different NAT answers `403` on every call although the key, the signature and the token are all correct. Enumerate the egress addresses of every host that will call HEVN — including the ones an autoscaler adds — before you create the production key.

The allowed sites are the third. A `redirectUri` the production window does not list is refused before any popup opens, so add the production callback origin before the first customer tries to sign in.

## What stops being instant

In the sandbox every row exists by the time the call returns. In production most of these are queues with humans or banks behind them.

| Step | Sandbox | Production |
| - | - | - |
| Client provisioning | seconds | seconds — when a linked client is still `provisioning`, poll `GET /dapi/v1/client` with `X-Hevn-Account` until `ready` |
| KYB decision | approved on completion | a review. Poll `GET /dapi/v1/client/kyb` with `X-Hevn-Account`; expect `rfi` at least as often as `approved` |
| Rail activation | `active` with account details immediately | `preparing` until the partner publishes the account details. Poll `GET /dapi/v1/banks` |
| Payin settlement | you call `/dapi/v1/sandbox/payins/{payinId}/complete` | the payer's bank moves on its own clock; the income row appears when it lands |
| Payout settlement | you call `/dapi/v1/sandbox/payouts/{payoutId}/status` | the partner drives it. `settled` means accepted and debited, not delivered |
| Ledger rows | written before the call returns | written when the money moves. Reconcile from `GET /dapi/v1/transactions`, not from the response you got an hour ago |

Every one of those is the same fix: a poller with backoff and a deadline, keyed on your own record. Both of the shapes you need are on the pages that produce them — `poll_until` in the guides and the full loop in [HTTP client](https://docs.gethevn.com/whitelabel/reference/client.md).

## What production starts refusing

Refusals your sandbox runs never produced, because the emulator never argues:

* **`422 contact_payment_details_invalid`** — the receiving bank does not recognise the beneficiary. Usually the name. Correct the contact with `PATCH /dapi/v1/contacts/{contactId}` and book again.
* **A virtual account that stays `preparing`** — the sandbox opens one as soon as nothing blocks it; production waits for the partner's review, for hours to days. Read `blockers[]` on `GET /dapi/v1/banks/{product}`: an empty list means the wait is the partner's, not yours.
* **`422 amount_below_minimum` and `422 amount_above_maximum`** — real rail minimums replace the sandbox's `0.10`.
* **`attention: {"kind": "rfi"}` on a payout** — the partner is holding the transfer pending a question. Nothing anyone signs moves it until the question is answered.
* **`503 provider_unavailable`** — a partner is down. Retry with backoff; do not re-book.
* **`429 rate_limited`** — the counters are enforced in production. Honour `Retry-After`. See [Limits](https://docs.gethevn.com/whitelabel/reference/limits.md#rate-limits).

## The checklist

**1. Point at the production host**

Set `HEVN_API` to `https://api.hevn.finance/dapi/v1`. Nothing else in your code changes.

**2. Get the flags**

Integrator profile, escrow if you need it, and the rails for your markets. Verify by listing rails for a real client, not by being told.

**3. Create a production developer key**

In the production app, with the production egress IPs in its allowlist. Save the private half the browser shows you to a file with mode `0600` on the machine that will hold it. Never copy the sandbox key, and never assume the sandbox allowlist carries over.

**4. Configure the production sign-in window**

In the production app, under **Settings → Partner program → Developer → Sign-in window**: your title, logo and colours, and every production origin your callback page is served from under **Allowed sites**. Check that your server binds each session's `state` to the browser and keeps the client id in its own session — [Sign in with HEVN](https://docs.gethevn.com/whitelabel/sign-in-with-hevn.md#3-bind-the-session-to-this-browser).

**5. Delete every /dapi/v1/sandbox call**

There are four, and all four answer `404` in production. Whatever they drove — a deposit, a payin settling, a payout moving — now happens because a bank did it.

**6. Replace instant reads with polling**

Client `ready`, KYB decided, rail `active`, payin credited, payout settled after its holder approved it. Back off, cap the interval, set a deadline, and record the state on your side before you act on it.

**7. Handle 429 and log X-Request-ID**

One HTTP helper that sleeps for `Retry-After`, re-mints once on `401`, and logs `X-Request-ID` on anything `5xx`. Support asks for that id first.

**8. Re-read your refusal handling**

The sign-in window's results — `access_denied`, `already_registered`, `already_linked`, `expired` — and, for payouts from your own account, the four funding codes: `already_funded` is success, `funding_in_progress` means wait, `funding_attempt_expired` means re-open with the same key, and `payment_slot_consumed` means this payout can never move money — book a new one. See [Errors](https://docs.gethevn.com/whitelabel/reference/errors.md#funding-a-payment).

**9. Rehearse losing the key**

Create a second key before you need it, log in with it, and book a payout from your own account naming the new `publicKey`. Rotation is a field on `POST /dapi/v1/payouts`, not a migration — but only if the second key already exists **and** its allowlist covers the hosts that will use it. Delete the old key once the new one has carried a request.

**10. Fund what production makes you fund**

Escrow refunds leave your own wallet, and a payout debits the client's. The sandbox faucet account covered both; now your balances and your customers' do.

> [!WARNING]
>
> Run one real payment end to end before you point customers at it: one client linked through the production window, one rail, one small payout to an account you control approved in the window, reconciled from `GET /dapi/v1/transactions`. Every difference on this page shows up in that one run.

> [card] [Next: Coverage](https://docs.gethevn.com/whitelabel/reference/coverage.md)
>
> The currencies money can arrive in, the markets it can leave for, and what decides availability.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.