Skip to main content
Ask your AI
Source code Package

Session source

Standalone session detection and management that can be composed with any walkerOS source.

Installation​

Install the packages:

npm install @walkeros/collector @walkeros/web-source-session

Configure in your code:

import { startFlow } from '@walkeros/collector';
import { sourceBrowser } from '@walkeros/web-source-browser';
import { sourceSession } from '@walkeros/web-source-session';

const { collector, elb } = await startFlow({
  sources: {
    browser: sourceBrowser,
    session: {
      code: sourceSession,
      config: {
        settings: {
          storage: true,
        },
      },
    },
  },
});

Detection methods​

The session source uses two complementary methods:

MethodStorageUse Case
WindowNonePrivacy-first, per-page session
StoragelocalStorageCross-page session tracking

Window-based detection​

Without storage, session detection relies on browser signals. A session starts on a page load that is a plain navigation, not a reload or back and forward, and that either carries marketing parameters or arrives from another host:

// Simplified: when a page load starts a session
const [navigation] = performance.getEntriesByType('navigation');
const isNewSession =
  navigation?.type === 'navigate' &&            // a plain navigation
  (hasMarketingParameters(location.href) ||     // UTM parameters or a click ID
    referrerHostname !== location.hostname);    // another site or subdomain, or no referrer (direct)

Reloads, back and forward, internal links and single-page-app route changes start no session, and neither does a browser without Navigation Timing.

Marketing parameters:

UTM and other marketing parameters trigger session start, even with an internal referrer. The following are recognized by default:

// UTM parameters → mapped to short names
utm_source    → source
utm_medium    → medium
utm_campaign  → campaign
utm_term      → term
utm_content   → content

// Click IDs → stored as clickId + original key
gclid, dclid, fbclid, msclkid, ttclid, twclid, igshid, sclid

To capture additional URL parameters, pass a parameters map. Each entry maps a URL parameter name to the property name it should be stored as in the session data:

session: {
  code: sourceSession,
  config: {
    settings: { storage: true },
    parameters: {
      ref: 'referral',           // ?ref=newsletter → { referral: 'newsletter' }
      affiliate_id: 'affiliate', // ?affiliate_id=abc → { affiliate: 'abc' }
    },
  },
}

Storage-based detection​

With storage: true, sessions persist across page loads:

session: {
  code: sourceSession,
  config: {
    settings: {
      storage: true,
      length: 30, // Session timeout in minutes
    },
  },
}

Session lifecycle:

┌─────────────────────────────────────────────────────────────┐
│ Session Timeline │
├─────────────────────────────────────────────────────────────┤
│ │
│ User visits Page 2 Page 3 30min idle │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ [Session 1] ─────────────────────────────► [Expires] │
│ id: abc123 │
│ │
│ User returns │
│ │ │
│ ▼ │
│ [Session 2] │
│ id: def456 │
└─────────────────────────────────────────────────────────────┘

Storage settings:

SettingDefaultDescription
sessionKeyelbSessionIdStorage key for the session ID
deviceKeyelbDeviceIdStorage key for the device ID
sessionStoragelocalStorage type for the session ID (local, session, cookie)
deviceStoragelocalStorage type for the device ID (local, session, cookie)
domainCookie domain, e.g. example.com to share IDs across subdomains (cookie storage only)

Session start event​

When a new session is detected, the source pushes a session start event. It carries source.type: 'session' with the page url and referrer, so destinations see the landing URL with its campaign parameters. In storage mode the source also sets user.session and user.device on the collector, so every event of the session carries them:

{
  name: 'session start',
  data: {
    isStart: true,           // Always true when this event fires
    isNew: true,             // True only on the device's very first session
    id: 'abc123',            // Session ID
    device: 'xyz789',        // Device ID (storage mode only)
    storage: true,           // Whether storage is active
    count: 5,                // Total session count for this device
    runs: 1,                 // Page views in this session
    start: 1704067200,       // Session start timestamp
    referrer: 'google.com',  // Hostname only (not full URL)
    marketing: true,         // Set when marketing parameters are present
    source: 'google',        // utm_source
    medium: 'cpc',           // utm_medium
    campaign: 'winter-sale', // utm_campaign
    clickId: 'gclid',        // Which click ID parameter matched (if present)
    platform: 'google',      // Ad platform of that click ID
    gclid: 'AW-123...',      // The actual click ID value
  },
  source: {
    type: 'session',
    platform: 'web',
    url: 'https://example.com/?utm_source=google&utm_medium=cpc',
    referrer: 'https://www.google.com/',
  }
}

isStart vs isNew:

FieldMeaning
isStart: trueA new session began on this page load
isNew: trueThis is the device's very first session ever (storage mode only)

A returning visitor starting their second session has isStart: true but isNew: false. isNew only appears in storage mode since window-only mode has no device memory.

The landing (navigation type, referrer, marketing parameters) is evaluated once per page load, so a route change in a single-page app that calls walker run does not start a new session, even when the new URL carries campaign parameters. Storage mode still counts each run in runs and starts a new session after inactivity.

User IDs only with storage: user.session and user.device are set only in storage mode, so they are present exactly when storage consent was given. In window mode the session ID would vanish on the next page load, so it is not a user ID and lives only in data.id of the session start event. Without storage consent, use a server-side key such as user.hash to group events.

Click ID parameters are stored twice: clickId identifies which platform originated the click (e.g. 'gclid'), and a separate field stores the actual value (e.g. gclid: 'AW-123...'). This makes it easy to filter by platform without inspecting the value.

Configuration​

This source uses the standard source config wrapper (consent, data, env, id, ...). For the shared fields see source configuration. Package-specific fields live under config.settings and are listed below.

Settings​

PropertyTypeDescriptionMore
storagebooleanEnable persistent storage for session/device IDs
consentstring | arrayConsent key(s) required to enable storage mode
lengthnumberSession timeout in minutes
pulsebooleanKeep session alive on each event
sessionKeystringStorage key for session ID
sessionStorage'local' | 'session' | 'cookie'Storage type for session
deviceKeystringStorage key for device ID
deviceStorage'local' | 'session' | 'cookie'Storage type for device
deviceAgenumberDevice ID age in days
domainstringCookie domain for session and device IDs, e.g. 'example.com' to share across subdomains (cookie storage only)
cbfunctionCustom session callback function or false to disable
clickIdsArray<object>Custom click-ID registry. Entries with a `param` matching a default override the platform name in place; new params append to the end of the priority list.

Mapping​

This package does not define custom rule-level settings. For the standard rule fields (consent, condition, data, batch, name, policy) see mapping.

Examples

New marketing session

A visit with UTM parameters starts a new session and emits walker user, session, and session start calls.

Event
{
  "storage": true
}
Out
elb("user", {
  "session": "s3ss10n-id",
  "device": "d3v1c3-id"
});

elb("session", {
  "id": "s3ss10n-id",
  "start": 1700000000000,
  "isNew": true,
  "count": 1,
  "runs": 1,
  "marketing": true,
  "source": "google",
  "medium": "cpc",
  "campaign": "winter-sale",
  "referrer": "",
  "device": "d3v1c3-id",
  "isStart": true,
  "storage": true,
  "updated": 1700000000000
});

elb({
  "name": "session start",
  "data": {
    "id": "s3ss10n-id",
    "start": 1700000000000,
    "isNew": true,
    "count": 1,
    "runs": 1,
    "marketing": true,
    "source": "google",
    "medium": "cpc",
    "campaign": "winter-sale",
    "referrer": "",
    "device": "d3v1c3-id",
    "isStart": true,
    "storage": true,
    "updated": 1700000000000
  },
  "source": {
    "type": "session",
    "platform": "web",
    "url": "https://example.com/?utm_source=google&utm_medium=cpc&utm_campaign=winter-sale",
    "referrer": ""
  }
})

Returning visitor

A returning visit with a google referrer reuses the stored device id and increments the session count.

Event
{
  "storage": true
}
Out
elb("user", {
  "session": "n3w-s3ss10n",
  "device": "d3v1c3-id"
});

elb("session", {
  "id": "n3w-s3ss10n",
  "start": 1700001000000,
  "isNew": false,
  "count": 3,
  "runs": 1,
  "referrer": "google.com",
  "device": "d3v1c3-id",
  "isStart": true,
  "storage": true,
  "updated": 1700001000000
});

elb({
  "name": "session start",
  "data": {
    "id": "n3w-s3ss10n",
    "start": 1700001000000,
    "isNew": false,
    "count": 3,
    "runs": 1,
    "referrer": "google.com",
    "device": "d3v1c3-id",
    "isStart": true,
    "storage": true,
    "updated": 1700001000000
  },
  "source": {
    "type": "session",
    "platform": "web",
    "url": "https://example.com/",
    "referrer": "https://google.com"
  }
})

Session without storage consent

Without storage consent the session runs in window mode: no user ids are set, the session id travels only as data.id of the session start event.

Event
{}
Out
elb("user", {});

elb("session", {
  "isStart": true,
  "storage": false,
  "start": 1700000000000,
  "id": "s3ss10n-id",
  "referrer": "",
  "marketing": true,
  "source": "google",
  "medium": "cpc",
  "campaign": "winter-sale"
});

elb({
  "name": "session start",
  "data": {
    "isStart": true,
    "storage": false,
    "start": 1700000000000,
    "id": "s3ss10n-id",
    "referrer": "",
    "marketing": true,
    "source": "google",
    "medium": "cpc",
    "campaign": "winter-sale"
  },
  "source": {
    "type": "session",
    "platform": "web",
    "url": "https://example.com/?utm_source=google&utm_medium=cpc&utm_campaign=winter-sale",
    "referrer": ""
  }
})

Session detection adapts to consent state:

Consent StateBehavior
No consentWindow-only detection (no storage)
Consent grantedStorage-based with device ID
Consent revokedFalls back to window-only
// Configure consent requirement
session: {
  code: sourceSession,
  config: {
    settings: {
      consent: 'analytics', // Wait for this consent key
      storage: true,
    },
  },
}

// Grant consent to enable storage
elb('walker consent', { analytics: true });

Pulse mode​

With pulse: true, the source updates the session's last-seen timestamp on each page load without counting it as a new page view and without triggering a new session start event. Use this for heartbeat-style presence tracking where you want to extend session lifetime without inflating runs:

session: {
  code: sourceSession,
  config: {
    settings: {
      storage: true,
      pulse: true,  // Extends session, does not increment runs
    },
  },
}

Custom session callback​

The cb setting lets you hook into session detection. It receives the detected session data, the source's collector interface, and the default callback. The interface carries only push (the source's pipeline) and command, with or without a consent gate, so collector state such as consent or user is not readable there:

session: {
  code: sourceSession,
  config: {
    settings: {
      cb: (session, collector, defaultCb) => {
        // Run custom logic first
        console.log('New session:', session.id);

        // Then run the default behavior (sets user IDs + pushes event)
        defaultCb(session, collector);
      },
    },
  },
}

Set cb: false to completely disable the default behavior. No user commands and no session start event will be emitted. This is useful when you want full control over what happens after detection.

Next steps​

💡 Need implementation support?
elbwalker offers hands-on support: setup review, measurement planning, destination mapping, and live troubleshooting. Book a 2-hour session (€399)