bankroll.session() resolves the Bankroll session token — a signed JWT scoped
to your app that identifies the current user. Send it to your server in the
x-bankroll-token header and verify it there before trusting anything in it.
session() as often as you like.
One call, one real person
Almost everything hard about running a real-money product — multi-accounting, bonus abuse, limits that actually hold, AML — reduces to one question: is this account a unique, real person? Answering it yourself means building identity verification and deduplication before you write a line of your actual product. On Bankroll, the answer is a parameter:session({ identity: true }) resolves only once the user has verified their
identity — and because a real person can only verify once, a verified
session is a single, Sybil-resistant identity. That one primitive is the
foundation the hard parts stand on:
- Multi-accounting — one verified identity maps to one person; a second account for the same human can’t produce a second verified session.
- Responsible gaming — enforce deposit, loss, and time limits per person, not per account.
- AML & eligibility — the identity is KYC-backed, with verified age and region.
verification_declined. Plain session() still works before verification —
get the handle and run free-to-play, then require { identity: true } at the
moment real money is on the line.
An older Bankroll app that can’t run verification rejects
{ identity: true }
with update_required.Send it to your server
The token travels in thex-bankroll-token header. Decorate fetch once and
every request carries it:
'unavailable') requests go out bare and your server
responds 401. Any other failure — including the user declining — propagates.
Verify it on your server
Never trust a session token that hasn’t been verified. In Node,verifyToken is
the entire route guard:
null/undefined and returns null on any failure, so one if (!session)
covers missing, forged, expired, and wrong-app tokens alike.
On other stacks, verify the JWT directly:
| What | Value |
|---|---|
| Algorithm | RS256 |
| Public keys (JWKS) | https://joinbankroll.com/.well-known/jwks.json |
Issuer (iss) | https://joinbankroll.com |
Audience (aud) | Your app’s origin (e.g. https://acme.example) |
The session
A verified token gives you the session and the user inside it:geo (where the user is
for this session — current location, not their residence). The user carries
who they durably are.
| Field | Where | Meaning |
|---|---|---|
iss / aud / iat / exp | session | Standard JWT envelope. iss is always https://joinbankroll.com; aud is your origin. |
geo | session | The user’s region for this session — an ISO 3166-2 code (e.g. US-NY), or a bare country code (US) when only the country is known. Omitted if unresolvable. |
user.wallet | user | The user’s wallet address. Use it as their primary key on your platform, and as the destination for payouts. |
user.username | user | The user’s Bankroll handle. Always present — the host guarantees one, so you never special-case a handle-less user. |
user.identity | user | Verification state, always present. { age } — verified (age omitted if their date of birth isn’t on file). false — not verified (pending, never started, or rejected). Truthy ⟺ one verified real identity. |
Wire names: in the raw JWT,
user.wallet is the sub claim and
user.identity is delivered as kyc. The SDK exposes the friendly names; if you
verify raw JWTs on another stack, read sub and kyc (treating an absent kyc
as not-verified).Gating eligibility
Real-money eligibility is a server-side decision, computed from the verified session — never from an unverified client value:session.user.identity(truthy) — the user has cleared verification (one real, deduplicated person).falsemeans not verified.session.user.identity.age— the user’s verified age, for age-restricted play.session.geo— the user’s region for this session, for where-you-can-play rules.
Handling a decline
A decline is a choice, not a failure.session() rejects with a
BankrollError whose code is consent_declined; session({ identity: true })
rejects verification_declined if they back out of verification. Catch either
and show your own “connect to continue” state rather than surfacing an error.