Checkpoint Docs
Detect

JavaScript Beacon

Full-featured client-side signal collection for AI agent detection, with Web Worker offloading

Overview

The Checkpoint JavaScript Beacon is a client-side SDK that collects browser and performance signals and sends them to Checkpoint for AI agent detection. It runs non-blocking, batches events, offloads work to a Web Worker when available, and queues events while offline.

The Beacon collects signals — classification happens server-side. It does not return a detection class on the client. View results in the dashboard. If you need a detection classification inline in a request, use server-side Middleware or the Gateway instead.

Key Features

  • Non-blocking — Collection runs on an interval and offloads to a Web Worker when available
  • Automatic batching — Efficient network usage with smart queue management
  • Offline resilience — Queues events when offline, sends when reconnected
  • Privacy-first — Honors Do Not Track by default; optional IP anonymization
  • TypeScript — Full type definitions included

Prerequisites

  • A Checkpoint Project ID — the constructor throws if it's missing. See Credentials for where to find it in the dashboard.
  • A JavaScript-executing browser context (SPA, static site, or any client-side app) — the Beacon has no server-side component.

Installation

npm install @kya-os/checkpoint-beacon
yarn add @kya-os/checkpoint-beacon
pnpm add @kya-os/checkpoint-beacon

Quick Start

import { CheckpointBeacon } from '@kya-os/checkpoint-beacon';

const beacon = new CheckpointBeacon({
  projectId: 'acme-corp', // same value as the pixel's data-project-id
});

// Record a page view (a periodic heartbeat also runs automatically)
beacon.collect('pageview');

projectId is required — the constructor throws if it is missing. Use the same Project ID as your pixel (a name-based slug like acme-corp, or a UUID). There is no endpoint option; the Beacon POSTs to https://kya.vouched.id/v1/beacon automatically.

How It Works

Constructing the Beacon immediately starts a periodic collection loop (a heartbeat every flushInterval). Call collect('pageview') or trackEvent(...) to record specific events. For each send, the Beacon picks the best available execution mode:

  1. Web Worker mode (when useWorker is enabled and the browser is capable) — processing runs off the main thread
  2. Fallback mode — main-thread execution scheduled via requestIdleCallback
  3. Direct mode — a direct transport send

Browser capabilities are detected automatically; if the Worker is unavailable or insufficient, the Beacon silently falls back.

Configuration

const beacon = new CheckpointBeacon({
  // Required
  projectId: 'acme-corp',

  // Segmentation
  environment: 'production',
  tags: { version: '2.0.0', region: 'us-east' },

  // Collection & batching
  flushInterval: 5000, // ms between automatic collections/flushes
  batchSize: 10, // events per batch
  maxQueueSize: 100, // max events queued (e.g. while offline)

  // Reliability
  retryAttempts: 3,
  retryDelay: 2000, // base retry delay in ms

  // Privacy
  respectDoNotTrack: true, // honor the browser DNT signal (default)
  anonymizeIp: false,

  // Worker
  useWorker: true, // auto-disabled in local dev — see below

  // Debugging
  debug: false,
  logLevel: 'error',
});

Configuration Options

OptionTypeDefaultDescription
projectIdstringRequiredProject ID (same value as the pixel's data-project-id)
environmentstringEnvironment tag for segmentation
tagsRecord<string, string | number | boolean>Custom segmentation tags
enabledbooleantrueMaster on/off switch
debugbooleanfalseVerbose console logging
logLevel'error' | 'warn' | 'info' | 'debug''error'Log verbosity
respectDoNotTrackbooleantrueWhen the browser sends DNT, the Beacon collects/sends nothing
anonymizeIpbooleanfalseRequest IP anonymization
sessionTimeoutnumber1800000Session lifetime in ms (30 minutes)
sessionCookieNamestring'_as_beacon_session'Session cookie name
batchSizenumber10Events per batch
flushIntervalnumber5000Interval (ms) for automatic collection/flush
maxQueueSizenumber100Max queued events
retryAttemptsnumber3Retry count for failed sends
retryDelaynumber2000Base retry delay (ms)
useWorkerbooleanenvironment-derivedUse a Web Worker; auto-disabled on localhost/dev/SSR
workerUrlstringCustom worker script URL
workerConfigobjectAdvanced worker tuning

respectDoNotTrack defaults to true. When the visitor's browser sends a Do Not Track signal, the Beacon collects and sends nothing. useWorker has no fixed default — it is enabled on production-like hosts but automatically disabled on localhost, 127.0.0.1, 192.168.*, *.local, Next.js dev mode, and during SSR (when window is unavailable).

Beacon API

new CheckpointBeacon(config)

Creates a beacon and starts periodic collection. Throws if config.projectId is missing.

collect(eventType?)

Collect signals and send an event. eventType defaults to 'pageview'. Valid types: 'pageview', 'pageunload', 'heartbeat', 'custom', 'error', 'performance'.

await beacon.collect('pageview');

trackEvent(name, metadata?)

Send a custom event with optional metadata. This is the correct way to attach your own data to a detection signal.

await beacon.trackEvent('button_click', {
  buttonId: 'signup-cta',
  page: '/home',
});

trackPageUnload()

Convenience helper that records a pageunload event (uses a reliable unload send). Call it from a beforeunload/pagehide handler.

window.addEventListener('pagehide', () => beacon.trackPageUnload());

updateConfig(updates)

Merge configuration at runtime. Changing useWorker/workerUrl/workerConfig restarts the worker; changing flushInterval restarts the collection loop.

beacon.updateConfig({ flushInterval: 10000 });

destroy()

Stop automatic collection, terminate the worker, flush pending events, and release resources.

beacon.destroy();

The Beacon is not an event emitter — there is no .on(...) API and no client-side detection event. It also has no start(), stop(), identify(), or getDetectionResult() method.

Web Worker Mode

When useWorker is enabled and the browser is capable, the Beacon offloads processing to a Web Worker so the main thread stays free for UI rendering. Signals are collected on the main thread and handed to the worker, which handles batching and network requests.

import { CheckpointBeacon } from '@kya-os/checkpoint-beacon';

const beacon = new CheckpointBeacon({
  projectId: 'acme-corp',
  useWorker: true,
});

Workers are automatically disabled in local development (localhost / Next.js dev) because dev servers typically don't serve the worker file. The Beacon works the same without a worker. If a bundler can't resolve the worker, set useWorker: false explicitly. If capabilities are insufficient or a restrictive CSP blocks workers, the Beacon falls back to the main thread automatically.

For advanced setups, the BeaconWorkerClient class is exported from @kya-os/checkpoint-beacon.

Data Collection

The Beacon gathers signals via two collectors that run automatically:

CollectorSignals collected
BrowserCollectorUser agent, language, platform, screen & viewport, timezone, hardwareConcurrency, deviceMemory, touch points, referrer, vendor
PerformanceCollectorNavigation timing, connection info, memory (Chrome), and paint timing (FCP/LCP)

Browser Compatibility

Modern evergreen browsers are supported. Web Worker offloading requires a browser with the Worker API; otherwise the Beacon falls back to the main thread.

BrowserMinimum Version
Chrome90+
Firefox88+
Safari14+
Edge90+

Offline Support

The Beacon handles intermittent connectivity:

  1. When offline, events are queued in an OfflineQueue (exported from the package)
  2. The queue holds up to maxQueueSize events
  3. When connectivity returns, queued events are sent automatically
  4. Failed sends are retried with backoff (retryAttempts / retryDelay)

Performance

The Beacon is designed to be lightweight: collection is non-blocking, events are batched, and work is offloaded to a Web Worker when available. For the current gzipped bundle size, see the npm package / bundlephobia badge rather than a hard-coded figure.

Network & CSP

The Beacon script loads from https://kya.vouched.id and POSTs detections to https://kya.vouched.id/v1/beacon. If your site restricts outbound requests (a Content Security Policy, a corporate proxy, or tracker blockers), allow that host.

For a CSP, add it to connect-src:

Content-Security-Policy: connect-src 'self' https://kya.vouched.id;

Framework Integration

React

import { useEffect } from 'react';
import { CheckpointBeacon } from '@kya-os/checkpoint-beacon';

function App() {
  useEffect(() => {
    const beacon = new CheckpointBeacon({
      projectId: process.env.NEXT_PUBLIC_CHECKPOINT_PROJECT_ID!,
    });
    beacon.collect('pageview');

    return () => {
      beacon.destroy();
    };
  }, []);

  return <div>Your app</div>;
}

Vue

import { onMounted, onUnmounted } from 'vue';
import { CheckpointBeacon } from '@kya-os/checkpoint-beacon';

let beacon: CheckpointBeacon;

onMounted(() => {
  beacon = new CheckpointBeacon({
    projectId: import.meta.env.VITE_CHECKPOINT_PROJECT_ID,
  });
  beacon.collect('pageview');
});

onUnmounted(() => {
  beacon?.destroy();
});

Troubleshooting

"Project ID is required for Checkpoint Beacon" thrown at construction

The constructor throws synchronously if config.projectId is missing or empty. Pass the same Project ID used elsewhere in your stack — see Credentials for where to find it.

No detections in the dashboard

  • Check the Network tab for a POST to https://kya.vouched.id/v1/beacon. If it's absent, the Beacon isn't sending; if it's present but the response is not a 2xx, the send is failing rather than the collection.
  • If the visitor's browser sends Do Not Track, the Beacon collects and sends nothing (respectDoNotTrack defaults to true) — this is silent by design, not an error.
  • Confirm the Project ID matches the project you're viewing in the dashboard.
  • A restrictive CSP, corporate proxy, or tracker blocker can block the request to kya.vouched.id — see Network & CSP above.

Worker failures don't show up anywhere

Worker initialization and worker-send failures are caught internally, and the Beacon falls back to the main thread automatically — this is expected behavior, not a bug. To see what's happening, set debug: true and logLevel: 'debug', then look for [Checkpoint Beacon]-prefixed console messages such as "Worker unavailable, using main thread" or "Worker failed, using fallback".

Beacon vs Pixel

For a lightweight, no-code alternative, see the Marketing Pixel.

FeatureBeaconPixel
Installationnpm packageScript tag / GTM
Signal richnessHigherBasic
Custom eventstrackEventLimited
Web WorkerYesNo
Offline supportYesNo
Best forApplicationsMarketing sites

Next Steps