Cookie Policy
Last updated: 17 August 2026
What Are Cookies?
Cookies are small text files stored on your device when you visit a website. We also use browser local storage for some preferences. This policy explains what we store, why, and how you can control it.
Your Choices
When you first visit NumDojo, you can accept all cookies, use essential cookies only, or open Manage preferences to choose Google Analytics and OpenAI Ads measurement separately. You can change either choice at any time via in the site footer. Essential cookies cannot be disabled because the platform cannot function without them.
Google Analytics stays blocked until you select it and sign in to an account recorded as adult. OpenAI Ads measurement has a separate choice and the same account-age boundary. Signed-out visitors can save choices for later, but neither service runs while they are signed out. NumDojo does not treat measurement consent as consent to future user-level ad personalisation.
Cookies We Set
| Name | Provider | Type | Purpose | Duration |
|---|---|---|---|---|
| accessToken | NumDojo | Essential | Keeps you signed in (short-lived session JWT) | 15 minutes |
| refreshToken | NumDojo | Essential | Securely renews your session without re-entering credentials | 7 days |
| _csrf | NumDojo | Essential | Protects against cross-site request forgery on state-changing requests | 1 hour |
| oauth_state_nonce | NumDojo | Essential | Validates OAuth sign-in state (Google / GitHub) | 10 minutes |
| _ga | Google Analytics | Optional measurement | Distinguishes unique visitors for usage statistics | 2 years |
| _ga_* | Google Analytics | Analytics (optional) | Persists session state for GA4 measurement | 2 years |
| numdojo_openai_ads_consent | NumDojo | Essential preference | Shares only your OpenAI Ads measurement choice with trusted server conversion checks | 180 days |
| __oppref, __obref | OpenAI Ads measurement | Optional measurement | Attributes an eligible adult conversion to an OpenAI ad without storing raw contact details | Not documented; disabled pending review |
Browser Local and Session Storage
These are not HTTP cookies. Local storage can remain after you close the browser, while session storage normally lasts for the current tab session. NumDojo does not apply a time-based expiry unless the table says otherwise.
| Key | Storage | Purpose | Retention |
|---|---|---|---|
| numdojo_cookie_consent | Local storage | Stores your separate Google Analytics and OpenAI Ads choices, when you updated them, and the consent format version | No programmed expiry. Replaced when you change your choices and retained until you clear site data. |
| authToken | Local storage | Short-lived access JWT copy for API and real-time features; httpOnly cookies remain the primary session store | The JWT expires after 15 minutes. The browser copy is replaced on refresh and removed on sign-out or an invalid session. An expired copy can remain until then or until you clear site data. |
| reducedMotion, soundEnabled, fontSize, highContrast, autoPlayAnimations, darkMode, colorBlindMode, animationSpeed, screenReaderEnabled, keyboardShortcuts, keyboardNavigationEnabled, focusIndicatorStyle, hapticFeedbackEnabled | Local storage | Stores accessibility, display, sound, animation, keyboard, focus, and haptic preferences | No programmed expiry. Replaced when a setting changes. darkMode can also be removed by the cache-clearing page. Otherwise retained until you clear site data. |
| ageThemeOverride | Local storage | Stores an age-group theme override on this device | No programmed expiry. Removed when the override is reset to automatic, or when you clear site data. |
| numdojo_sound_settings | Local storage | Stores detailed sound volume and mute settings | No programmed expiry. Replaced when a sound setting changes and retained until you clear site data. |
| numdojo_haptic_settings | Local storage | Stores haptic feedback status and intensity | No programmed expiry. Replaced when a haptic setting changes and retained until you clear site data. |
| numdojo:trachtenberg-x11-best-score | Local storage | Stores your best score for the multiplication-by-11 worksheet on this device | No programmed expiry. Replaced when you check a worksheet and retained until you clear site data. |
| numdojo:virtual-soroban-practice | Local storage | Stores your virtual Soroban best run, daily streak, and last practice day on this device | No programmed expiry. Updated after a correct answer. A missed day resets the streak when you next record a correct answer. Retained until you clear site data. |
| numdojo_battle_analytics | Local storage | Stores completed Battle session and problem-attempt history, including answers, accuracy, scores, and response times | No time-based expiry. Limited to the newest 100 completed sessions; older sessions are dropped as new ones are added. Otherwise retained until you clear site data. |
| battle_failed_sessions | Local storage | Stores Battle session payloads that could not be sent to the server so they can be retried | No time-based expiry. Limited to 50 sessions. A session is removed after a successful retry; otherwise it remains until you clear site data. |
| username | Local storage | A legacy value read to identify your own entry in some league and guild views; the current frontend does not create or update it | No current write or deletion path. A value left by older code can remain until you clear site data. |
| battle_snapshot | Session storage | Stores the current Battle room, scores, problem, and timestamp for short reconnection recovery in the same tab | Recovery accepts it for 30 seconds. It is removed when a Battle ends normally, while the browser clears any remaining value when the tab session ends or you clear site data. |
| redirectAfterLogin | Session storage | Records the protected route present when sign-in is required; current navigation uses the next URL parameter rather than reading this key | No earlier removal is programmed. The browser clears it when the tab session ends, or it is removed when you clear site data. |
Third-Party Cookies
Stripe (payments)
When you subscribe or manage billing, Stripe Checkout and the Customer Portal may set their own cookies on stripe.com for fraud prevention and payment processing. See Stripe's Privacy Policy.
Google Analytics
Only loaded after you select Google Analytics and sign in to an account recorded as adult. IP addresses are anonymized and ad signals are disabled in our GA4 configuration. Measurement ID: G-3P9MDPCB6X.
OpenAI Ads measurement
This is loaded only after you separately select OpenAI Ads measurement, sign in to an account recorded as adult, and an approved campaign Pixel ID is configured. Registration matching uses a one-way hash of the email address. Raw email addresses are not sent through the measurement event, and events are opted out of future user-level personalisation. OpenAI's current measurement documentation does not state how long the __oppref and __obref cookies last. OpenAI Ads measurement remains disabled until that duration and the related retention handling are reviewed.
Managing Cookies in Your Browser
You can also block or delete cookies through your browser settings. Disabling essential cookies will prevent you from staying signed in. Clearing local storage will reset UI preferences, worksheet and virtual Soroban practice figures, and your cookie consent choice (the banner will appear again).
Contact
Questions? Contact us at support@numdojo.com.