JavaScript tag events

Complete reference of every web event generated by the JavaScript tag.

Basic event structure

Every event shares the same top-level envelope:

Event envelope structure
{
  "dt": 1772115228563,
  "event": "<event-name>",
  "payload": { ... },
  "session_id": "551790ef-...",
  "anonymous_user_id": "671c8bdc-...",
  "user": { "email": "user@example.com" },
  "fingerprint": ["v1_<hash>"],
  "_app_id": 4153,
  "_ip_addresses": ["94.230.145.34"],
  "contexts": { ... }
}
Field Type Notes
dt Number Unix timestamp (ms) when the event was captured client-side
event String Event type name (see sections below)
payload Object Event-specific data
session_id UUID Session identifier, rotated after session_timeout_ms of inactivity
anonymous_user_id UUID Stable anonymous user identifier persisted in localStorage
user Object? Present only if betterstack('user', {...}) was called. Contains whatever was passed.
fingerprint Array? Present only if FingerprintJS is configured. Array with one "v1_<hash>" string.
_app_id Number Internal app identifier (added server-side)
_ip_addresses Array Client IP addresses (added server-side)
contexts Object? Browser/OS/device info. Present on autocapture events only (not on memory, user-agent-specific-memory, session-start, or custom events).

contexts object

Event context data
{
  "browser": { "type": "browser", "browser": "Chrome 145", "name": "Chrome", "version": "145.0.0.0" },
  "client_os": { "type": "os", "os": "macOS 10.15.7", "name": "macOS", "version": "10.15.7" },
  "device": { "type": "unknown" }
}

Shared payload.meta for autocapture events

All autocapture events such as click, dblclick, contextmenu, tap, input, change, submit, reset, page-load, page-change, page-leave, rage-click include this meta block in payload:

Event metadata
{
  "title": "Page Title",
  "timestamp": 1772115228563,
  "timezone": -60,
  "url": "https://example.com/page",
  "userAgent": "Mozilla/5.0 ...",
  "referrer": "https://example.com/previous",
  "screen": { "width": 3008, "height": 1692 },
  "window": { "width": 1504, "height": 1528 },
  "devicePixelRatio": 2,
  "language": "en",
  "platform": "MacIntel",
  "isMobile": false,
  "isTouch": false,
  "isBot": false
}

Tracking custom events

Created with the betterstack('track', '<name>', <data>) API call.

Custom event structure
{
  "event": "custom-event-name",
  "payload": { "sampleKey": "sampleValue", ... }
}

Events

Event Description
session-start Marks the beginning of a new session. Includes UTM params, referrer, and fingerprint data.
page-load Initial full page load (window.load).
page-change In-app SPA navigation (pushState, replaceState, popstate, hashchange).
page-leave Tab close or full navigation away (beforeunload).
click Left-click on non-touch devices.
dblclick Double-click on non-touch devices.
contextmenu Right-click (all devices).
tap Touch start on touch devices.
rage-click 5+ clicks within 8px / 2s — indicates user frustration.
input Keystroke or value change in a form element.
change Form element value committed (blur after change, or select/checkbox toggle).
submit Form submission.
reset Form reset.
memory JS heap snapshot via performance.memory (Chrome-only), polled every 15s.
user-agent-specific-memory Detailed memory breakdown via performance.measureUserAgentSpecificMemory(), polled every 60s.
<custom> Any event sent via betterstack('track', name, data).

session-start

First event in a new session, or after session timeout. Fired lazily: queued when the first trackable event occurs, not on page load itself. If fingerprinting is configured, waits for fingerprint resolution up to 500ms before sending.

The session is new when there is no existing sid in localStorage, or when the time since last activity exceeds session_timeout_ms.

session-start event
{
  "event": "session-start",
  "payload": {
    "utm_source": null,
    "utm_medium": null,
    "utm_campaign": null,
    "utm_term": null,
    "utm_content": null,
    "referrer": "https://google.com/",
    "gclid": null,
    "fbclid": null,
    "msclkid": null,
    "ttclid": null,
    "li_fat_id": null,
    "twclid": null,
    "page_url": "https://example.com/landing",
    "fingerprint_data": { "...": "..." }
  }
}

fingerprint_data is only present when fingerprinting is enabled; contains raw component data.

page-load

window.load event (fires once per full page load). Enabled when autocapture_enabled? and autoPageview !== false.

page-load event
{
  "event": "page-load",
  "payload": {
    "event": "page",
    "timestamp": "2026-02-26T14:13:48.563Z",
    "meta": { "...shared meta..." },
    "session": "551790ef-...",
    "details": {
      "type": "page-load",
      "url": "https://example.com/"
    }
  }
}

Note: meta.referrer is the browser's document.referrer (the previous page that linked here).

page-change

In-app navigation via:

  • history.pushState() (SPA routers: React Router, Vue Router, Next.js, Turbo)
  • history.replaceState() (only if URL actually changes)
  • popstate (browser back/forward)
  • hashchange (hash-based routing)

Deduplicated: skipped if URL hasn't changed since last capture.

page-change event
{
  "event": "page-change",
  "payload": {
    "event": "page",
    "timestamp": "2026-02-26T14:13:48.589Z",
    "meta": { "...shared meta...", "referrer": "https://example.com/previous-spa-url" },
    "session": "551790ef-...",
    "details": {
      "type": "page-change",
      "url": "https://example.com/new-page"
    }
  }
}

Note: meta.referrer is the previous SPA URL, not document.referrer. The onEventCapture callback in AutocaptureConfigGenerator moves details.referrermeta.referrer.

page-leave

window.beforeunload event: tab close, full navigation away, page refresh.

page-leave event
{
  "event": "page-leave",
  "payload": {
    "event": "page",
    "timestamp": "...",
    "meta": { "...shared meta..." },
    "session": "551790ef-...",
    "details": {
      "type": "page-leave",
      "url": "https://example.com/current-page"
    }
  }
}

Special behavior: page-leave skips _ensureSessionStarted(): it won't create a new session on its own.

click

document.click event on non-touch devices (!('ontouchstart' in window)). Enabled when autocapture_enabled?.

click event
{
  "event": "click",
  "payload": {
    "event": "click",
    "timestamp": "...",
    "meta": { "...shared meta..." },
    "session": "551790ef-...",
    "target": {
      "selector": "a",
      "attributes": { "href": "https://example.com/about" }
    },
    "details": {
      "type": "click",
      "x": 652,
      "y": 89
    }
  }
}

target.selector is the element tag name. target.attributes contains whitelisted attributes configured in AutocaptureConfigGenerator: value, type, href, src, id, name, placeholder, title, alt, role.

dblclick

document.dblclick event on non-touch devices.

Same payload structure as click, but details.type = "dblclick" and payload.event = "click".

contextmenu

document.contextmenu event, right-click. Fires on all devices. no touch guard.

Same payload structure as click, but details.type = "contextmenu".

tap

document.touchstart event on touch devices.

Same payload structure as click, but details.type = "tap" and coordinates come from the touch event. Only fires on touch-capable devices.

rage-click

5+ clicks within 8px radius over 2 seconds. Detected by monitoring all document.click events and calculating centroid clustering. Config: Enabled when autocapture_enabled? AND rage_clicks_enabled?.

rage-click event
{
  "event": "rage-click",
  "payload": {
    "event": "rage-click",
    "timestamp": "...",
    "meta": { "...shared meta..." },
    "session": "551790ef-...",
    "details": {
      "type": "rage-click",
      "clickCount": 5,
      "firstClickAt": 1772115228000,
      "lastClickAt": 1772115229500,
      "centroid": { "x": 652, "y": 89 }
    }
  }
}

After detection, the click buffer is reset.

input

document.input event: fires on every keystroke/value change in form elements.

input event
{
  "event": "input",
  "payload": {
    "event": "form",
    "timestamp": "...",
    "meta": { "...shared meta..." },
    "session": "551790ef-...",
    "target": {
      "selector": "input",
      "attributes": { "type": "text", "id": "name", "name": "name", "placeholder": "Your name" }
    },
    "details": {
      "type": "input"
    }
  }
}

Note: the value attribute is captured for select elements but the actual typed text in text inputs is NOT captured, only the element attributes are collected via the safelist.

change

document.change event:fires when a form element loses focus after its value changed, or on select/checkbox/radio change.

Same structure as input, but details.type = "change". For select elements, target.attributes.value reflects the newly selected value.

submit

document.submit event: form submission.

submit event
{
  "event": "submit",
  "payload": {
    "event": "form",
    "timestamp": "...",
    "meta": { "...shared meta..." },
    "session": "551790ef-...",
    "target": {
      "selector": "form",
      "attributes": { "id": "contact-form", "name": "contact" }
    },
    "details": {
      "type": "submit",
      "action": "https://example.com/api/submit",
      "method": "post",
      "id": "contact-form",
      "name": "contact"
    }
  }
}

details includes form metadata such as action, method, id, name extracted from target.form.

reset

document.reset event, when form reset button clicked.

Same structure as submit, but details.type = "reset".

memory

Polled every 15 seconds via setInterval. Also fires once immediately on init. Uses performance.memory (Chrome-only, non-standard).

memory event
{
  "event": "memory",
  "payload": {
    "memory": {
      "jsHeapSizeLimit": 4294967296,
      "totalJSHeapSize": 4078952,
      "usedJSHeapSize": 2979308
    },
    "userAgent": "Mozilla/5.0 ...",
    "meta": { "url": "https://example.com/page" }
  }
}

No contexts field. Completely different payload structure from autocapture events: no event, timestamp, session, target, or details fields inside payload.

user-agent-specific-memory

Polled every 60 seconds via setInterval. Also fires once immediately on init. Uses performance.measureUserAgentSpecificMemory() which requires crossOriginIsolated context (COOP/COEP headers).

user-agent-specific-memory event
{
  "event": "user-agent-specific-memory",
  "payload": {
    "bytes": 1234567,
    "breakdown": [
      {
        "bytes": 1000000,
        "attribution": [{ "url": "https://example.com/", "scope": "Window" }],
        "types": ["JavaScript"]
      }
    ],
    "userAgent": "Mozilla/5.0 ...",
    "meta": { "url": "https://example.com/page" }
  }
}

No contexts field. The bytes and breakdown fields come directly from the performance.measureUserAgentSpecificMemory() API result, spread into the payload.

Need help integrating the JavaScript tag?

We're here for you! Let us know at hello@betterstack.com and we'll be happy to help.