Dashboard

Browser SDK

@sentriq/browser collects coarse, non-invasive browser signals and submits them to Sentriq. The server is authoritative for device identity — this SDK never asserts a device_id; it only reports signals and an opaque local-storage hint.

Install

npm install @sentriq/browser

Sentriq.init(options)

import { Sentriq } from '@sentriq/browser';

const sentriq = await Sentriq.init({
  publicKey: 'pub_test_xxx',
  // Optional. Defaults to the hosted Sentriq API.
  apiUrl: 'https://api.sentriq.example',
  // Optional. Defaults to 5000ms.
  timeoutMs: 5000,
});

Only pass a public key (pub_test_... / pub_live_...). Sentriq.init rejects anything that doesn't look like one — secret keys must never ship to browser code.

track(eventName, options?)

Reports a named security event. eventName is one of: 'page_view' | 'signup' | 'login' | 'login_failed' | 'logout' | 'password_reset' | 'two_factor_challenge' | 'sensitive_action' | 'payment' | 'custom'.

const result = await sentriq.track('login', {
  // Your own opaque reference for this end user — never a password,
  // email, or other sensitive value. Optional, max 128 characters.
  account: { id: 'usr_123' },
});

result.deviceId;         // == result.device.id
result.eventId;          // == result.event_id
result.risk.score;       // 0-100
result.risk.level;       // 'low' | 'medium' | 'high' | 'critical'
result.risk.decision;    // 'allow' | 'monitor' | 'challenge' | 'block'
result.risk.signals;     // which signals contributed

account.id is validated client-side (max 128 chars) before any request is sent, mirroring the server's own validation — an oversized value throws a SentriqConfigurationError immediately.

identify()

A thin convenience wrapper around track('page_view').

const result = await sentriq.identify();

resetIdentity()

Clears the SDK's locally-stored client_hint, so the next call reports a brand-new one — useful for an explicit "sign out and forget this device" action.

sentriq.resetIdentity();
Not a guaranteed fresh device
Confidence-based device resolution can still recognize the same device from other matching signals even with a new client_hint. Resetting has no retroactive effect on past events or devices, and it shouldn't be called routinely — that defeats the point of the hint.

Errors

track() / identify() never fail silently — they always reject with a typed error, all extending SentriqRequestError:

  • SentriqConfigurationError — bad SDK usage caught client-side before any request is sent (e.g. an invalid public key, an oversized account.id).
  • SentriqNetworkError — the request itself failed (DNS, connection, CORS).
  • SentriqTimeoutError — the request exceeded timeoutMs.
  • SentriqApiError — the server responded with a non-2xx status; exposes .status and, when the server sent one, a .message.
import { SentriqRequestError } from '@sentriq/browser';

try {
  await sentriq.track('login', { account: { id: userId } });
} catch (e) {
  if (e instanceof SentriqRequestError) {
    // handles all four typed errors
  }
}

What's collected

Coarse signals only — every call collects:

  • Timezone, locale, and language list.
  • Screen dimensions, color depth, pixel ratio.
  • hardwareConcurrency / deviceMemory when the browser exposes them.
  • Platform string and user agent.
  • Touch support.
  • Storage capability booleans (localStorage/sessionStorage/IndexedDB/cookies) — capability flags only, never storage contents.
  • Three coarse automation heuristics, never treated as proof: navigator.webdriver, a plugin/mime-type headless pattern, and a runtime/viewport consistency check.
  • A client_hint: a random opaque token generated once and persisted in localStorage.

What's deliberately not collected

No canvas/audio/WebGL fingerprinting, no installed-font or plugin enumeration, no cross-site tracking, no IP address (the server derives that from the request itself), no page content, no form input, no credentials.