Skip to main content
Your backend holds one credential: a P-256 private key. HEVN stores only the public half. That key logs you in to /dapi/v1 and signs every payment.

Create it in the app

Keys are created in the HEVN app — https://sandbox.gethevn.com, or https://app.gethevn.com in production — under Settings → Partner program → Developer keys. Nothing in this API creates one. The key is what authorizes money from every wallet you control, so creating it takes a browser session of your own, never a token this API minted. Creation asks for two things, and both are frozen the moment the key exists: There is no edit and no PATCH. A new egress range, or one more scope, means a new key. The browser generates the keypair and shows you the private half once. Save it then: HEVN never holds it and cannot show it again. Write it to a file with mode 0600 on the machine that will use it, out of images, repositories and logs. The app adds the public half to your account’s signer in the same action, so there is nothing else to wire up. One key covers every client — a client’s wallet is deployed with your signer already attached, so no per-client setup exists. An account can hold up to five keys at once, which is what makes a rotation possible without downtime.

What the key is

PEM armor in a publicKey field is rejected, base64url is rejected, and a raw 64-byte r‖s signature — what WebCrypto and Java’s P1363 mode produce — is rejected. Convert to DER before you base64-encode. The details are in Signing.

Scopes

A scope names one class of signing right. You choose them when the key is created and they never change afterwards. Those are the only four values. Everything else on /dapi/v1 — reading a balance, booking a payout, opening a rail, filing KYB, walking the ledger — is open to any platform session, so scopes gate the confirm step and the contact book rather than the API. A route whose scope the key does not hold answers 403 forbidden with two fields: details.requiredScope is the scope the route wants, details.scope is what the key actually holds. Nothing is prepared and nothing is signed.
Choose by what the deployment does. A service that books payouts and confirms them needs payout:sign and nothing else; the job that maintains the contact book needs recipient:write and cannot move money with it.
A scope limits which routes the key’s session may call. It does not cap how much money a permitted route may move. A key holding payout:sign can confirm a payout of any size, from any client wallet you control. Amount and approval controls are yours to build — see Accounts and control.

The IP allowlist

The allowlist is the set of source addresses the key may be used from. HEVN canonicalises every entry and drops duplicates, so 203.0.113.10 is stored as 203.0.113.10/32. Every request re-checks it. A call from an address outside the list is 403, and so is a call whose source address HEVN cannot determine — on a read as on a payment.
The allowlist cannot be edited. Enter the addresses your deployment will actually call from, including the ones an autoscaler adds. A perfectly good key and a perfectly good signature still answer 403 from an address you did not list, and a new NAT gateway is an expensive way to find that out.
Your edge must strip caller-supplied X-Forwarded-For, X-Real-IP and CF-Connecting-IP before they reach HEVN, or the allowlist is checking a header the caller controls.

Load it in your server

Every example on these pages reads the key from HEVN_KEY_PEM — the file you saved when the app showed you the private half.
A successful POST /dapi/v1/auth/challenge is the proof the key is live and the allowlist covers this host. See Sessions.

When a key is refused

Login answers the same refusal for every key problem — an unknown email, a key that no longer exists, a call from an address outside the allowlist, a signer that is not attached, a bad signature — so an attacker learns nothing from probing: Once you are authenticated the refusals are specific, because by then HEVN knows who you are: The full list is in Errors.

Rotate a key

Keys are immutable and additive, so a rotation is a roll rather than an edit, and both keys work while both are live.
1

Create the second key

In the app, with the same scopes and an allowlist covering the hosts that will use it. Save the private half. Nothing changes for running code.
2

Switch your servers

Deploy the new key file. Payments start carrying the new publicKey. An approval is bound to your signer rather than to one key, so anything prepared just before the switch can still be confirmed with either key while both are live.
3

Delete the old key

In the app. The old key stops logging in and stops authorizing money on the next request that uses it.
Deleting a key does cut the sessions already minted from it. Every platform request re-reads the key, so the next call on an access token minted from a deleted key is a 403 — as is the POST /dapi/v1/auth/refresh that would have replaced it. That is the control to reach for when a token leaks, not only when a key does.

Habits worth keeping

  • Keep the private key in a secret manager and mount it at runtime; keep it out of images, repositories and logs.
  • One key per environment, and one per deployment if you can — the publicKey on each payment tells you which one signed.
  • Give each key the narrowest scopes and the narrowest allowlist its job allows, and create the replacement before you move traffic to a new range.
  • Never sign in a browser. The key can spend from every client balance you control; a browser is the wrong place for it.
  • Rehearse the roll before you need it. A rotation you have never run is not a control.

Next: Sessions

Two calls to log in, and one header to act as any of your clients.