Identity & Sessions
Every web event includes three identifiers:
visitor_id— a stable UUID that persists across visits (~1 year). Identifies a browser, not a person.session_id— a UUID that resets after 30 minutes of inactivity. Used for funnels, “sessions per visitor” metrics.user_id— your application’s user ID, attached only when you callLoguro.identify(...). Lets you join analytics back to your auth database.
Visitor ID
Generated on first event of a fresh browser. Stored in localStorage by default under the key loguro_v. Survives:
- Tab close, browser restart
- Going to a different domain and coming back (same origin = same key)
- App reinstall (no —
localStorageis cleared)
Does not survive:
- Different browser
- Private / incognito window
- User clears site data
Loguro.reset()call
Storage strategies
data-analytics-id-storage="localStorage" <!-- default -->
data-analytics-id-storage="cookie" <!-- cross-subdomain -->
data-analytics-id-storage="memory" <!-- per-tab, never persists --> localStorage (default)
Per-origin storage. Simple, fast, doesn’t show up in browser cookie listings (less consent-banner pressure). Use this unless you specifically need cross-subdomain.
cookie
Use when you have multiple subdomains (www.acme.com + app.acme.com) and want a single visitor identity across them:
data-analytics-id-storage="cookie"
data-analytics-domain=".acme.com" The leading . makes the cookie valid across all subdomains. Cookie attributes: Path=/, SameSite=Lax, 1-year expiry.
memory
A fresh visitor_id per tab load. Useful for sites with strict privacy requirements where any persistence is unacceptable (or for testing). Reduces visitor analytics to mostly useless — only session-level data makes sense.
Session ID
A new session starts on the first event of a fresh browser. Each subsequent event refreshes lastSeen. After 30 minutes without any event, the next one starts a new session.
This means:
- Typical user (active browsing): 1 session per visit
- User who tabs away for an hour and comes back: 2 sessions
- User who reads a long article without clicks or scroll events: session might expire mid-read
If you want to count “engaged time” in long articles, send a heartbeat event every 5 minutes:
setInterval(() => {
window.Loguro.track('article_heartbeat', { article_id: '...' });
}, 5 * 60 * 1000); User ID
Optional. Set it whenever you have one — typically right after login:
window.Loguro.identify('user_42'); From that point forward, every event includes user_id: "user_42". Past events are not retroactively updated — they remain anonymous (only visitor_id + session_id). This is intentional: events live in immutable Parquet storage.
With traits
window.Loguro.identify('user_42', {
plan: 'pro',
signed_up_at: '2025-11-10',
team_id: 'team_acme'
}); If traits are provided, a custom event called identify is fired with those traits. Useful for tracking plan changes, role updates, etc.
Persistence
By default user_id is in-memory only — set fresh on every page load. This is the safe default: if your user logs out by closing the tab, the next visitor on the same browser doesn’t inherit the previous identity.
Opt into persistence:
data-analytics-persist-user="true" Now user_id survives reloads. Useful for SPAs that don’t reload between auth boundaries, but make sure you call Loguro.reset() on logout.
Logout flow
function logout() {
// Your auth cleanup
await api.logout();
localStorage.removeItem('auth_token');
// Reset analytics identity
window.Loguro.reset();
// Optional: redirect
window.location.href = '/';
} Loguro.reset() wipes:
visitor_id(generates a new one on next event)session_iduser_id- Cookie identity (if
id-storage="cookie")
The next pageview belongs to a brand-new visitor with no link to the previous one.
When identify() should be called
| Scenario | Call identify() here |
|---|---|
| User logs in | Right after successful auth |
| User signs up | After account creation, before redirect to onboarding |
| App initializes with existing session (cached auth token) | On every page load if you already know who they are |
| User upgrades plan | Call identify() again with updated traits |
| User logs out | Call Loguro.reset(), not identify(null) |
Wire shape — full
A custom event after identify() looks like:
{
"event_type": "custom",
"custom_event_name": "subscription_upgraded",
"url": "https://app.acme.com/billing",
"path": "/billing",
"visitor_id": "01HZ7XK4N8...",
"session_id": "01HZ7XK4P2...",
"user_id": "user_42",
"custom_props": { "new_plan": "pro", "old_plan": "starter" },
"timestamp": "2026-05-14T10:00:00.000Z"
} Privacy notes
- IP and User-Agent are never sent in the body — they’re extracted server-side from request headers. The server uses them for geo + device parsing, then discards them.
- We do not fingerprint. No canvas, no audio, no WebGL, no installed fonts.
visitor_idis a UUID, not derived from any user signal. Random per browser.
See Privacy & Consent for GDPR, DNT, cookie-consent flows.