Skip to main content

Prerequisites

Calm authenticates Privy users via Privy’s identity tokens — short-lived JWTs that carry the user’s sub and linked_accounts (including the wallet and email). They are not the same as Privy access tokens. You only need to flip this once per Privy app.
1

Open the Privy dashboard

Sign in at dashboard.privy.io and pick your app.
2

Navigate to User Management → Authentication → Advanced

Privy User Authentication settings panel, Advanced tab selected

Privy dashboard: User management → Authentication, Advanced tab

3

Toggle "Return user data in an identity token" ON

Scroll the Advanced tab until you see it. Once enabled, the user’s linked_accounts (including the linked wallet) ride inside the identity token — which is exactly what the SDK forwards to Calm.
Return user data in an identity token toggle, enabled

"Return user data in an identity token" toggle, enabled

If this toggle is off, the SDK can’t start a session — the API can’t see the user’s wallet in the JWT claims and returns wallet_not_linked.

Generating the publishable key

The calmKey you pass to <PrivyCalmProvider> is a publishable key minted in the Calm dashboard. A Privy key is bound to your Privy app ID, so Calm only accepts identity tokens minted by that Privy app.
1

Open the Calm dashboard

Sign in to the Calm dashboard and open Publishable keys.
2

Fill out "Generate a new key"

  • EnvironmentSandbox to start, Live for production. Each environment issues its own key (calm_public_sandbox_… / calm_public_live_…).
  • Wallet provider — select Privy.
  • Privy app ID — paste your Privy app ID (from the Privy dashboard). Required for Privy keys; it binds the key to your Privy app.
  • Allowed origin (optional) — the origin your app is served from. Live keys require an https:// origin; in Sandbox you can leave it blank to skip the Origin check (e.g. for http://localhost).
Calm dashboard generate-key form with the Privy wallet provider and a Privy app ID

Calm dashboard: Generate a new key, Privy selected

3

Generate and copy the key

Click Generate. The new key appears under Active keys — copy it and pass it as calmKey. It looks like calm_public_sandbox_<32 hex>.
A generated publishable key listed under Active keys with a copy button

Calm dashboard: the new key under Active keys

Installation

To add Calm to your project, install the required packages.
  • Privy is the identity provider the SDK reads the user, wallet, and identity token from.
  • TanStack Query is an async state manager that handles requests, caching, and more.

Import the stylesheet

Import the Calm stylesheet once at your app root (Next.js layout.tsx, React main.tsx):
or @import it from your own CSS file:

Wrap App in <PrivyCalmProvider>

Place <PrivyCalmProvider> inside <PrivyProvider> and <QueryClientProvider>.
app/layout.tsx
Mount <PrivyCalmProvider> only once the signed-in user has a connected wallet — it reads the wallet address at mount and requires it to be defined. For email logins Privy provisions the embedded wallet as a separate step after authentication, so authenticated can be true while the wallet address is still undefined; mounting in that window throws. Gate the mount on usePrivy().user?.wallet?.address.

Open the onramp

The provider always renders its children. Wrap any trigger element in <CalmOnramp> to open the deposit modal, and gate it on the Calm session being ready via useSession — the trigger only enables once the session is live, so the modal’s data loads immediately on open. (A connected wallet alone isn’t enough; the session is what the modal’s requests are authenticated with.)

Props

string
required
Your publishable key — calm_public_(live|sandbox)_<32 hex>. Identifies the Calm tenant. Sent to the API on every session creation via the X-Calm-Publishable-Key header.
"usd" | "gbp" | "eur"
required
Source fiat currency for the bank-deposit onramp.
number
default:"1337"
Destination chain id for the delivered USDC. Defaults to HyperCore (1337).
string
default:"https://api.calmtreasury.xyz"
Override the Calm API root. Use https://api.sandbox.calmtreasury.xyz for the sandbox environment.
Partial<Record<number, string>>
required
Per-chain RPC URLs (keyed by chain id) used to read transaction receipts when confirming a swap. The receipt is read from the chain the swap transaction lands on (e.g. Base 8453), so each source chain needs a reliable RPC — reading through the wallet’s own endpoint can stall the confirm step. Provide an entry for every chain you support. Pass a stable (memoized) object.

Errors

The provider’s createSession throws on any non-2xx response. The useSession hook surfaces the error in result.error. See Errors for the full code table; the most common from this shell: