Skip to main content
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.
The first call may ask the user to connect your app; later calls resolve without re-prompting. Tokens are short-lived (~15 minutes) — the SDK caches the current one and mints a fresh one before it expires, and concurrent calls share one mint, so call 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.
If the user isn’t verified yet, Bankroll walks them through verification and resolves once they’re done; if they don’t complete it, the promise rejects with 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 the x-bankroll-token header. Decorate fetch once and every request carries it:
In a plain browser (status '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:
It checks the RS256 signature against Bankroll’s public keys, the issuer, and — critically — that the token was minted for your origin. It accepts 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:
WhatValue
AlgorithmRS256
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)
Always check aud equals your own origin, byte-for-byte — https, lowercase host, no default port, no trailing slash. The token is scoped to your app, so a token minted for a different app must not sign a user into yours. Also fetch the keys from the JWKS URL and cache them — never hard-code a key.
See the Quickstart for full verification snippets in Node.js, Ruby, and Python.

The session

A verified token gives you the session and the user inside it:
The session carries the envelope and the request’s geo (where the user is for this session — current location, not their residence). The user carries who they durably are.
FieldWhereMeaning
iss / aud / iat / expsessionStandard JWT envelope. iss is always https://joinbankroll.com; aud is your origin.
geosessionThe 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.walletuserThe user’s wallet address. Use it as their primary key on your platform, and as the destination for payouts.
user.usernameuserThe user’s Bankroll handle. Always present — the host guarantees one, so you never special-case a handle-less user.
user.identityuserVerification 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). false means 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.