Intunix

Identity & sessions

The SDK needs an identity to key cadence records against — “once per user”, “quiet for 30 days after they answer”. Most visitors are anonymous, so it maintains one for them, and hands the records over when they log in.

Anonymous ids

On first visit the SDK mints a random anonymous id and stores it in localStorage under intunix:anon. It is used whenever no user has been identified:

code
const uid = user?.id ?? anonId()

It is a random opaque string. It contains nothing about the person, is not shared across devices or browsers, and is never sent anywhere except on the responses and events that visitor produces.

identify()

Call it when you know who the user is — at login, and on every app boot where the session is already authenticated.

code
client.identify('user_42', { plan: 'pro', role: 'admin', signupYear: 2024 })

The traits become available to trait targeting. They accept strings, numbers, booleans, and null.

Traits are in-memory only

They are not persisted across page loads. If you only call identify() inside your login handler, then after any reload the user is anonymous again and every trait-based targeting rule stops matching. Call it wherever your app establishes who the user is.

What it does to existing records

This is the important part. identify() does not merely start writing under a new key — it moves everything recorded under the anonymous id onto the real one.

code
// Anonymous visitor dismisses the NPS form. Recorded under intunix:anon.
// They log in:
client.identify('user_42')
// The dismissal now belongs to user_42. The anonymous key is empty again.

So the anonymous id only ever holds activity since the last identify(). A form someone dismissed while logged out still counts against them after they log in, which is what you want — otherwise logging in would be a way to be re-asked immediately.

reset()

Call it on logout. It does four things:

  • Forgets the identified user and their traits.
  • Rotates the anonymous id to a fresh one.
  • Ends the current session.
  • Clears sticky context.
It is not what keeps users’ cooldowns separate

A common misconception. identify() already handles that, because it drains the anonymous key on every login — a returning user's caps live under their own id, not a shared anonymous one. If you call identify() on every login, gating stays correct even if you never call reset().

So what does skipping it cost?

It matters on a shared browser, where a second person uses the same device:

Without reset()Consequence
Session is not endedThe next person lands inside the previous session (30-minute TTL), so every_session forms will not re-fire for them.
Sticky context survivesStored in sessionStorage, so the previous user's context attaches to the next person's responses until the tab is closed.
Anonymous id is not rotatedBoth people's logged-out responses carry the same anonymous_id, so reporting counts two humans as one respondent.

The last one is the reason to bother. The first two self-heal when the session expires or the tab closes. A response, though, is written once and keeps whatever ids it had at submit time — permanently.

reset() matters more if your logout is a client-side transition with no page reload. The identified user is held in memory, so without it the SDK keeps attributing responses to the previous user until the next identify() lands.

What reset() deliberately keeps

The form catalog, and the per-device firstSeenAt and sessionCount that eligibility.minSessions and minDaysSinceFirstSeen gate on. Those describe the device, not the account. Clearing them would let anyone reset themselves to a brand-new user by logging out.

Sessions

A session lasts 30 minutes of inactivity. Tracked events and navigation renew it; a longer gap starts a new session and increments the session count.

A new session triggers three things:

  • The form catalog is re-fetched (once, for the registered form set).
  • Sticky context is cleared.
  • every_session cadences re-arm.

What is stored on the device

KeyStorageContents
intunix:anonlocalStorageAnonymous id.
intunix:sessionlocalStorageCurrent session id and its 30-minute expiry.
intunix:profilelocalStorageFirst-seen timestamp, session count, per-form outcomes.
intunix:shownlocalStoragePer-form show counts and timestamps — the cadence ledger.
intunix:experiences:v2localStorageCached form catalog, stamped with session and registered set.
intunix:contextsessionStorageSticky context bag.

No cookies are set, and nothing is written to a third-party domain. If storage is unavailable the SDK falls back to memory — forms still show, but cadence resets on every page load.

Putting it together

auth.ts
// On login — and on every boot where the user is already authenticated.
function onAuthenticated(user) {
  client.identify(user.id, {
    plan: user.plan,
    role: user.role,
  })
}

// On logout.
function onLogout() {
  client.reset()
}