Prerequisites
- A React 18 or 19 application.
- A publishable key for your workspace (
pk_live_…orpk_test_…) and a JWT secret — generate both in your workspace’s API keys section (see Publishable Keys & Secrets). - A backend that can sign a short-lived JWT with that secret. The secret must never reach the browser.
1. Install
2. Supply a session token
Every SDK request carries a short-lived HS256 bearer token. The SDK never mints it — it calls yourgetToken callback and sends whatever you return. How you produce it depends on where the SDK is running.
In your own app: your backend signs it
Sign a short-lived JWT with your JWT secret on your server, and expose an endpoint your frontend can call. Statisfy requires the standardiat / exp claims plus the end user’s email, which is how the gateway resolves who is asking.
In a Statisfy Portal: <PortalAuthGate> handles it
A hosted portal has no backend of its own, so its customers sign in passwordless: Statisfy emails a one-time code, and the exchange returns a session token plus a rotating refresh token that keeps them signed in across reloads.
You don’t implement any of that. Wrap your portal in <PortalAuthGate> — it renders the sign-in form, owns the token lifecycle, and supplies StatisfyProvider internally, so nothing below it touches getToken:
customer_id. A code is only ever sent to an address that matches; a request for any other address succeeds silently and sends nothing, so the sign-in screen can’t be used to probe your customer list.
The session token is short-lived (default 1 hour).
<PortalAuthGate> re-mints it silently before it expires using the stored refresh token, so customers stay signed in without seeing the form again.createPortalAuthClient export gives you the same token lifecycle without the React components.
3. Wrap your app in StatisfyProvider
StatisfyProvider supplies the gateway URL and authentication to every SDK component via React context. Set it up once, high in your tree:
4. Render your first component
Now any SDK component works anywhere beneath the provider — no connection props needed.5. Theming (optional)
The SDK’s styles are self-contained and ship inside@layer, so they won’t collide with your app’s CSS — and any plain, unlayered rule you write wins over them without !important. There are two override mechanisms:
--dw-*CSS variables (~31 tokens: colors, radius, elevation, focus rings, status colors, motion), set on:rootor any ancestor. Colors are space-separated RGB triplets, not hex.data-dw-partanatomy selectors to restyle exactly one element.
data-dw-theme="dark" (or "auto" to follow the visitor’s OS) on your html element. Without the attribute the widget stays light.
See Theming for the full variable tables, the guaranteed part names, and the dark palette.
Troubleshooting
401 user_token_invalid
401 user_token_invalid
The bearer token’s signature didn’t verify. Make sure
getToken returns the session token (in a portal, the one from /auth/verify — not the opaque refresh token), that it hasn’t expired, and that you minted it against the same tenant/environment your gateway points at.403 not_a_customer
403 not_a_customer
The signed-in user’s email isn’t a person on any account in your tenant. Add them as a contact, or sign in as a known customer.
Components throw 'must be rendered inside <StatisfyProvider>'
Components throw 'must be rendered inside <StatisfyProvider>'
An SDK component is mounted outside the provider. Move it beneath
<StatisfyProvider> — or, in a portal, beneath <PortalAuthGate>, which supplies the provider itself.