npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@callforge/tracking-client

v0.12.5

Published

Lightweight client library for the CallForge tracking API. Handles location-aware phone number assignment with aggressive caching and preload optimization.

Readme

@callforge/tracking-client

Lightweight client library for the CallForge tracking API. Handles location-aware phone number assignment with aggressive caching and preload optimization.

Installation

npm install @callforge/tracking-client
# or
pnpm add @callforge/tracking-client

Lease Hardening Migration (v0.8+)

This release adds bootstrap-token support for lease hardening and bot suppression.

Client integration requirements:

  • Add the generated preload snippet in <head>. This prefetches bootstrap tokens and keeps session lookup fast.
  • Keep handling phoneNumber and leaseId separately. A request can return a phone number with leaseId: null when lease assignment is intentionally suppressed.
  • For attribution/scale metrics, treat leaseId as the source of truth for lease-backed traffic.

Realtime Data Layer Upgrade (v0.10+)

This release adds explicit browser helpers for the new realtime layer:

  • client.linkPhoneCall({ phoneNumber, realtime }) for strict web-click to phone-call linkage plus optional realtime fields.
  • client.refreshPresenceLink({ phoneNumber, signal }) for short-lived active-visitor presence linking.
  • Automatic presence refreshes on load, debounced scroll, focus, visibility return (visibilitychange to visible), and SPA route changes (enabled by default).

Integration checklist:

  • Call linkPhoneCall immediately before opening a tel: link.
  • Pass the exact dialed number string (+1...) used by the link.
  • Continue dialing even if linkPhoneCall fails (best-effort attribution assist, not UX-blocking).
  • When ZIP is known, include it in linkPhoneCall(...realtime) as webZip and webZipSource.
  • Use realtime.params for additional key/value fields you want persisted with the linked session.
  • Keep default auto presence enabled unless you have a specific reason to disable it.

Quick Start

1. Add preload snippet to <head> (required for deterministic leases)

For optimal performance and lease hardening, add this snippet to your HTML <head>:

import { getPreloadSnippet } from '@callforge/tracking-client';

const snippet = getPreloadSnippet({ categoryId: 'your-category-id' });
// Add snippet to your HTML <head>

Generated HTML:

<link rel="preconnect" href="https://tracking.callforge.io">
<script>/* preload script */</script>

2. Initialize and start session + location requests

import { CallForge } from '@callforge/tracking-client';

const client = CallForge.init({
  categoryId: 'your-category-id',
  // endpoint: 'https://tracking-dev.callforge.io', // Optional: override for dev
});

const { session, location } = client.getSessionAsync();

// Location is delivered independently (often faster than phone number assignment)
console.log(await location);
// {
//   city: "Woodstock",
//   state: "Georgia",
//   stateCode: "GA",
//   zipOptions: ["30188", "30189", "30066", ...] // may be []
// } or null

// Phone session data (deterministic token + phone number)
console.log(await session);  // { sessionToken, leaseId, phoneNumber }

3. Deterministic click/callback attribution (optional)

If you initiate calls programmatically (click-to-call / callback), you can request a short-lived callIntentToken. CallForge will consume this once to deterministically map the call back to the web session that requested it.

// In the browser:
const { callIntentToken } = await client.createCallIntent();

// Send to your backend and attach to the call you initiate
await fetch('/api/callback', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ callIntentToken }),
});

Notes:

  • callIntentToken is short-lived and single-use.
  • Treat it as an opaque secret (do not log it).

4. Realtime call-link with bundled realtime fields (recommended)

Use linkPhoneCall to create the strict call-link and optionally attach realtime fields in the same request.

const client = CallForge.init({ categoryId: 'your-category-id' });

async function dialTrackedNumber(phoneNumber: string, webZip?: string) {
  try {
    // Default TTL is 30s (clamped server-side to 5-120s).
    await client.linkPhoneCall({
      phoneNumber,
      realtime: webZip
        ? {
            webZip,
            webZipSource: 'manual', // or 'suggested'
            params: {
              zipChoiceMethod: 'picker',
            },
          }
        : undefined,
    });
  } finally {
    // Do not block dialing on telemetry failure.
    window.location.href = `tel:${phoneNumber}`;
  }
}

Notes:

  • linkPhoneCall is optimized for mobile tap-to-call timing.
  • realtime.webZip accepts manual (typed) and suggested (picked from options) via webZipSource.
  • realtime.webZip is validated client-side (12345 format).
  • realtime.params allows additional key/value payloads for future realtime enrichment without API changes.

5. GA4 Integration

To enable GA4 call event tracking, CallForge needs the GA4 client_id for the visitor (from the _ga cookie). Optionally provide your GA4 Measurement ID to improve client ID capture reliability when Google Analytics loads late.

const client = CallForge.init({
  categoryId: 'your-category-id',
  ga4MeasurementId: 'G-XXXXXXXXXX', // Recommended
});

Requirements:

  1. Google Analytics 4 must be installed on your site and allowed to set the _ga cookie (gtag.js or GTM).
  2. Configure GA4 credentials in CallForge dashboard (Measurement ID + API secret).

How it works:

  • Extracts the GA4 client_id from the _ga cookie and sends it to CallForge as ga4ClientId.
  • Polls briefly after init to capture the cookie if Google Analytics sets it slightly later.
  • If ga4MeasurementId is configured and gtag is available, also uses gtag('get', measurementId, 'client_id', ...) (with a short retry window).
  • If ga4ClientId becomes available after a session is created, the client will refresh once to sync it to CallForge.

Manual override:

client.setParams({
  ga4ClientId: '1234567890.1234567890',
});

6. Track conversion parameters (optional)

The client automatically captures ad platform click IDs from the URL:

  • gclid - Google Ads
  • gbraid - Google app-to-web (iOS 14+)
  • wbraid - Google web-to-app
  • msclkid - Microsoft/Bing Ads
  • fbclid - Facebook/Meta Ads

You can also add custom parameters:

client.setParams({
  customerId: '456',
  landingPage: 'pricing',
});

// Parameters are automatically sent with every API request
await client.getSession();

API Reference

CallForge.init(config)

Initialize the tracking client.

interface CallForgeConfig {
  categoryId: string;        // Required - which number pool to use
  endpoint?: string;         // Optional - defaults to 'https://tracking.callforge.io'
  siteKey?: string;          // Optional - cache partition key (defaults to window.location.hostname)
  ga4MeasurementId?: string; // Optional - GA4 Measurement ID (e.g., 'G-XXXXXXXXXX')
  presenceAutoLink?: {       // Optional - defaults shown below
    enabled?: boolean;             // default true
    ttlSeconds?: number;           // optional override for auto refreshes
    scrollDebounceMs?: number;     // default 1200
    minRefreshIntervalMs?: number; // default 15000
    fireOnLoad?: boolean;          // default true
    fireOnScroll?: boolean;        // default true
    fireOnRouteChange?: boolean;   // default true (pushState/replaceState/popstate/hashchange)
  };
}

When presenceAutoLink.enabled is true, the client also sends auto presence refreshes on window focus and when document.visibilityState returns to visible.

client.getSession()

Get tracking session data (phone number + deterministic session token). Returns cached data if valid, otherwise fetches from the API.

interface TrackingSession {
  sessionToken: string;      // Signed, opaque token used to refresh the session
  leaseId: string | null;    // Deterministic assignment lease ID (when available)
  phoneNumber: string | null;
}

Behavior:

  • Returns cached data if valid.
  • Fetches fresh data when cache is missing/expired.
  • If loc_physical_ms is present in the URL, cached sessions are only reused when it matches the cached locId.
  • If lease assignment is suppressed (for example bot traffic or missing/invalid bootstrap), phoneNumber may be present while leaseId is null.
  • Throws on network errors or API errors.

client.getLocation()

Get location data only. Returns cached data if valid, otherwise fetches from the API.

const location = await client.getLocation();
// { city, state, stateCode, zipOptions } or null
interface TrackingLocation {
  city: string;
  state: string;
  stateCode: string;
  zipOptions?: string[]; // ordered by proximity, may be []
}

client.getSessionAsync()

Kick off both requests and use each as soon as it resolves.

const { session, location } = client.getSessionAsync();

location.then((loc) => {
  // show city/state ASAP
  // optionally render loc?.zipOptions in a ZIP picker
});

session.then((sess) => {
  // show phone number when ready
});

client.createCallIntent()

Create a short-lived call intent token for click/callback deterministic attribution.

const intent = await client.createCallIntent();
console.log(intent.callIntentToken);

client.linkPhoneCall(input)

Create a short-lived realtime call-link intent keyed by dialed number, with optional realtime payload fields persisted against the linked session.

const result = await client.linkPhoneCall({
  phoneNumber: '+13105551234',
  ttlSeconds: 30, // Optional, default 30s
  realtime: {
    webZip: '30309',          // Optional
    webZipSource: 'manual',   // Optional, defaults to 'manual' when webZip is provided
    params: {                 // Optional
      zipChoiceMethod: 'picker',
    },
  },
});

console.log(result.status); // 'ready'

Use this right before tel: navigation so inbound call handling can perform strict 1:1 consume.

Behavior:

  • Rejects invalid realtime.webZip values unless they are 5 digits.
  • Persists bundled realtime values (webZip + params) during call-link intent creation.
  • Mirrors bundled realtime values into custom params and performs a best-effort session sync backup.

client.refreshPresenceLink(input)

Refresh a short-lived presence link for active web visitors on a dialed number.

await client.refreshPresenceLink({
  phoneNumber: '+13105551234',
  signal: 'scroll', // Optional: 'load' | 'scroll' | 'focus' | 'visibility'
  ttlSeconds: 45,   // Optional, server-clamped
});

Note:

  • You usually do not need to call this directly because the client auto-refreshes presence on load, debounced scroll, and SPA route changes.
  • Use this method for explicit/manual refresh points only (for example custom engagement events).

client.onReady(callback)

Subscribe to session ready event. Callback is called once session data is available.

client.onReady((session) => {
  // session is the same TrackingSession object
});

client.onLocationReady(callback)

Subscribe to location ready event. Callback is called once location data is available.

client.onLocationReady((location) => {
  // location is { city, state, stateCode, zipOptions } or null
});

client.setParams(params)

Set custom tracking parameters for conversion attribution.

client.setParams({
  customerId: '456',
  landingPage: 'pricing',
  campaign: 'summer-sale',
});

Behavior:

  • Merges with existing params (later calls override earlier values).
  • If a session already exists (cached sessionToken), the client will refresh once to sync updated params server-side (best-effort).
  • Parameters are sent with every getSession() API request.
  • Persisted in localStorage alongside session data.

getPreloadSnippet(config)

Generate HTML snippet for preloading tracking data.

import { getPreloadSnippet } from '@callforge/tracking-client';

const html = getPreloadSnippet({
  categoryId: 'your-category-id',
  endpoint: 'https://tracking.callforge.io', // Optional
  siteKey: 'example.com', // Optional
});

Tracking Parameters

Auto-Capture

The client automatically extracts these parameters from the URL:

| Parameter | Source | |-----------|--------| | gclid | Google Ads Click ID | | gbraid | Google app-to-web (iOS 14+) | | wbraid | Google web-to-app | | msclkid | Microsoft/Bing Ads Click ID | | fbclid | Facebook/Meta Ads Click ID |

API Request Format

Parameters are sent as a sorted query string for cache consistency:

/v1/tracking/session?categoryId=cat-123&fbclid=456&gclid=abc&loc_physical_ms=1014221&sessionToken=...

Caching Behavior

  • Session cache key: cf_tracking_v1_<siteKey>_<categoryId>
  • Location cache key: cf_location_v1_<siteKey>
  • Bootstrap cache key: cf_bootstrap_v1_<siteKey>_<categoryId>
  • TTL: controlled by the server expiresAt response (currently 30 minutes)
  • Storage: localStorage (falls back to memory if unavailable)

Error Handling

try {
  const session = await client.getSession();
  if (session.phoneNumber) {
    // Use phone number
  } else {
    // No phone number available
  }
} catch (err) {
  // Network error or API error
  console.error('Failed to get tracking session:', err);
}

TypeScript

Full type definitions are included:

import type {
  CallForgeConfig,
  TrackingSession,
  TrackingLocation,
  TrackingParams,
  ReadyCallback,
  LocationReadyCallback,
  CallIntentResponse,
  CallLinkIntentResponse,
  LinkPhoneCallInput,
  RealtimeLinkPayload,
  RealtimeProfileSource,
} from '@callforge/tracking-client';

Environment URLs

| Environment | Endpoint | |-------------|----------| | Production | https://tracking.callforge.io (default) | | Staging | https://tracking-staging.callforge.io | | Dev | https://tracking-dev.callforge.io |