User identification

Out of the box PennyLens tracks every visitor anonymously. The moment you know who they are — they sign up, log in, or land from a magic link — call identify and we'll stitch every prior anonymous event in the session to that identity.

This page covers when to call identify, what to send, how anonymous sessions merge, and how to handle logout and consent.

The mental model

Every browser session starts with a rotating, first-party anonymous ID. The SDK persists it in a single localStorage entry until you either call identify (which links it to a user ID) or reset (which discards it).

The anonymous→known transition is retroactive: every event captured before the identify call in the current session is re-attributed to the user ID once you make the call. You don't lose any of the pre-signup behavioral data.

When to call identify

Call it as early as you can confirm the user's identity:

  • Signup — immediately after the account-creation request succeeds.
  • Login — on every successful authentication, including silent SSO refreshes on app load.
  • Magic-link arrivals — once you've verified the token on landing.
  • Server-rendered apps — emit the call from your post-login page or pass the identity through a server-rendered <script> block.
PennyLens.identify("user-7281", {
  email: "alex@example.com",
  plan: "pro",
  created_at: "2026-03-12T14:30:00Z",
});

Traits

The second argument is a property bag describing the user. Traits are stored at the user level — not the event level — so they appear on every event from that user going forward.

| Convention | Example | Notes | | --- | --- | --- | | email | "alex@example.com" | Used to display readable names in the dashboard. | | name | "Alex Rivera" | Optional. Falls back to email if absent. | | plan | "pro" | Free-form. Use for segmentation in funnels and cohorts. | | created_at | ISO 8601 string | Used by the activation-gap and retention detectors. | | company | "Acme Corp" | For B2B SaaS dashboards. |

Custom traits are accepted at any depth and become filter dimensions on the dashboard the moment they first appear.

Anonymous→known merging

Within a single session, identify retroactively stitches events to the user ID — no special handling required. Across sessions, the behavior depends on the device:

  • Same device, same browser — the next session starts with the user ID still cached, so every event is attributed from the first page load. Recordings, heatmaps, and funnels all see one continuous user.
  • New device or browser — the session starts anonymous until the user signs in again. Once identify fires, the new session's events join the user's history alongside the previous device's events.
  • Shared device (e.g. household, in-store kiosk) — call reset on logout so the next user starts fresh.

Logout and reset

On logout, call reset to clear the cached user ID and start a new anonymous session:

PennyLens.reset();

This is essential on:

  • Shared devices.
  • Apps where two users may authenticate from the same browser.
  • Consent flows where the user withdraws permission to be tracked.

Without reset, the next visitor on that device — including a different person on the same machine — will be attributed to the previous user until they call identify themselves.

GDPR considerations

User IDs are personal data. A few practical notes:

  • Right to deletion. When a user requests deletion through your account flow, send the same identifier to the data deletion API and we'll purge all events, recordings, and aggregates tied to that user within 30 days.
  • Lifecycle. Use a stable, opaque identifier (an internal user UUID) — never email, phone, or any identifier that's also customer data. This way deletion requests can be honored without touching email logs.
  • Pseudonymization. If you can't pass a stable ID without revealing PII, hash it server-side (e.g. SHA-256 of the user UUID) and pass the hash to identify. The dashboard will still display traits like email and name because they're stored as separate properties.

Cross-device identification

PennyLens doesn't perform device fingerprinting. Cross-device stitching only happens once the user calls identify from each device — exactly the same way most analytics tools work, but explicit about it. The anonymous session before login on a new device stays anonymous until the user authenticates.

Common patterns

React app on login

import { useEffect } from "react";

export function useTrackedSession(user: { id: string; email: string; plan: string } | null) {
  useEffect(() => {
    if (user) {
      window.PennyLens.identify(user.id, {
        email: user.email,
        plan: user.plan,
      });
    } else {
      window.PennyLens.reset();
    }
  }, [user]);
}

Server-rendered identification on page load

<script>
  window.__USER__ = {
    id: "<%= user.id %>",
    email: "<%= user.email %>",
    plan: "<%= user.plan %>",
  };
</script>

<script>
  PennyLens.ready(() => {
    if (window.__USER__) {
      PennyLens.identify(window.__USER__.id, {
        email: window.__USER__.email,
        plan: window.__USER__.plan,
      });
    }
  });
</script>

Next steps