The two credentials
How the flow works
Your backend holds the Org-API-Key and exchanges it for a short-lived member token scoped to one of your users. The app uses that member token as a Bearer credential for data calls, and asks your backend for a fresh one when it nears expiry.Step 1: Exchange (server-side)
Your backend callsexchangeToken with the Org-API-Key. The request identifies
the member by your own stable id, and may set profile fields used elsewhere
(display name, email, and the member’s tier).
string
required
Your stable identifier for the member. Scopes the minted token to this user.
string
Optional display name stored on the member profile.
string
Optional email stored on the member profile.
string
Optional tier for the member; flows into eligibility.
TokenGrant:
string
required
The short-lived member token. Hand this to the app.
string
required
ISO-8601 expiry. Pass it to the SDK as
tokenExpiresAt(Ms) so it can refresh
proactively.Step 2: Use and refresh (in the app)
Initialize the SDK with the member token, its expiry, and arefreshToken hook
that calls back to your backend (which holds the Org-API-Key) to mint a new
token. The hook returns the new token and expiry.
The refresh contract
The SDKs implement refresh identically:1
Proactive
Before a request, if the token expires within
refreshSkew (default 30s),
the SDK calls refreshToken first and uses the new token.2
Reactive (once)
If a request still returns
401, the SDK calls refreshToken once and
retries the request a single time with the new token.3
Failure propagates
If
refreshToken throws, the SDK does not retry it - the error propagates so
your app can send the user back through your own auth. A failed refresh is
never retried in a loop.Provide
tokenExpiresAt(Ms) whenever you can. Without it the SDK cannot refresh
proactively and falls back to reactive (401-triggered) refresh only, which adds
one round-trip on the first expired call.Security checklist
Org-API-Key is only ever read on your backend.
The app receives only member tokens, with their expiry.
refreshToken calls your backend, not the Expys exchange endpoint directly.Member tokens are short-lived; do not persist them beyond their expiry.