Skip to content

SDKs and the CLI

npm packages for the browser, your server and React Native, plus a command-line tool.

Which one do you need

The script tag is still the fastest way to start and it is not a lesser option: it is the same tracker, the same wire format and the same numbers. Reach for a package when a tag is the wrong tool rather than as an upgrade.

PackageUse it when
(none — script tag)An ordinary website. One line of HTML, no build step.
@termind/browserA bundled app that wants types, or a consent flow that must not load a third-party script until the visitor agrees, or a CSP that forbids external scripts.
@termind/nodeServer-side events an ad blocker cannot eat, revenue reported from the handler that took the payment, and AI crawler tracking.
@termind/react-nativeA React Native or Expo app. Same identity as your website, so a web-to-app funnel is one journey rather than two.
@termind/cliChecking an install from the machine that deployed it, and pulling numbers into scripts.

Browser

ts
npm install @termind/browser
ts
import { createTermind } from "@termind/browser";

export const termind = createTermind({
  projectKey: "pk_your_project_key",
  // Page views fire automatically, including on client-side navigation.
});

termind.track("signed_up", { plan: "pro" });
termind.identify("user_123", { email: "maya@example.com" });

Localhost is ignored unless you pass trackLocalhost: true, so development never pollutes real numbers.

For a consent banner, construct it with disabled: true and create it properly once the visitor agrees — or simply defer the import. Nothing is sent and no identifier is stored while disabled.

Your server

ts
import { createTermindServer } from "@termind/node";

const termind = createTermindServer({ apiKey: process.env.TERMIND_API_KEY! });

// An event that an ad blocker can never remove.
termind.track("trial_started", { personId: "user_123" }, { plan: "pro" });

// Money, reported by the same code that charged the card.
await termind.revenue({
  personId: "user_123",
  amountMinor: 4900,
  currency: "EUR",
  plan: "Growth",
});

// Serverless functions exit fast; flush before returning.
await termind.flush();

Use the same pk_ key the tracking script uses — Termind issues one key per website, found under Settings, then Tracking. It is safe in public HTML, which is why the browser package uses it too.

Seeing AI crawlers

An assistant fetches your HTML and leaves without running any JavaScript, so no browser SDK will ever see one. Call this from your middleware and Termind decides what is worth keeping.

ts
// middleware.ts
import { createTermindServer } from "@termind/node";

const termind = createTermindServer({
  apiKey: process.env.TERMIND_API_KEY!,
  projectKey: process.env.NEXT_PUBLIC_TERMIND_KEY!,
});

export async function middleware(request: Request) {
  const url = new URL(request.url);
  await termind.crawl({
    path: url.pathname,
    userAgent: request.headers.get("user-agent"),
    // Forward the IP so the claim can be confirmed. A user-agent is a string
    // anyone can set; without an address it cannot be verified.
    ip: request.headers.get("x-forwarded-for")?.split(",")[0] ?? null,
  });
}

React Native and Expo

ts
import AsyncStorage from "@react-native-async-storage/async-storage";
import { createTermindNative } from "@termind/react-native";

export const termind = createTermindNative({
  projectKey: "pk_your_project_key",
  storage: AsyncStorage,
  appVersion: "2.1.0",
  platform: "ios",
});

termind.screen("Pricing");
termind.track("subscribed", { plan: "pro" });
await termind.identify("user_123");

Storage is your choice — AsyncStorage, MMKV, expo-secure-store — because the package will not force a dependency on an app that already made that decision. Anything with getItem, setItem and removeItem works.

Call identify with the same id your website uses and the two are one person. That is what makes a web-to-app funnel readable.

Command line

bash
npx @termind/cli check yoursite.com

  https://yoursite.com  →  200
  ✓ Installed via the script tag
  ✓ Key pk_abc123

It fetches the page and reads the served HTML rather than asking our API, because "the dashboard is empty" is usually a deploy that dropped the tag, and only the page itself can settle that. It exits non-zero when the snippet is missing, so it works as a deploy check.

bash
export TERMIND_API_KEY=pk_your_project_key

termind stats --days 7
termind events --limit 20
termind track deploy_completed
termind stats --json | jq .visitors
NextScript configurationEvery attribute the snippet accepts, and the full browser API.