Documentation

Up and tracking in 60 seconds.

Paste the snippet. Refresh your site. Watch events stream into the dashboard. Everything you need — install, config, API, plugins — is on this page.

Step 01

Install the SDK

Two ways to install — pick the one that matches your stack. Both ship the same framework-agnostic core (@observejs/browser), ~10KB gzipped, zero dependencies.

Option A — Script tag (any site)

index.html
<script>
  window.__OBSERVE_ID = "obs_pk_live_xxx";
  import("/observe.js");
</script>

Option B — npm package

terminal
npm install @observejs/browser
# Framework wrappers
npm install @observejs/react     # React
npm install @observejs/nextjs    # Next.js
npm install @observejs/vue       # Vue
Step 02

Initialize once at bootstrap

Call Observe.init() once — the SDK is idempotent, SSR-safe (no-ops on the server), and never throws into your app.

app.ts
import { Observe } from "@observejs/browser";

Observe.init({
  apiKey: "obs_pk_live_xxx",
  environment: "production",
});

Observe.page();
Observe.identify("user_123", { email: "ada@example.com" });
Observe.track("Signup Completed", { plan: "pro" });
Step 03

Configuration options

All options accepted by Observe.init(config). Only apiKey is required.

OptionTypeDefaultDescription
apiKeystring ★—Project tracking key (publishable).
apiUrlstringobservejs.lovable.app/api/publicIngestion base URL.
environmentstring"production"e.g. "production", "staging", "dev".
releasestring—App version / git SHA for source maps.
debugbooleanfalseVerbose console logging.
batchSizenumber20Events queued before auto-flush.
flushIntervalnumber ms5000Periodic auto-flush interval.
offlineQueuebooleantruePersist queue to localStorage.
sampling0..11Drop events outside the sample rate.
sessionTimeoutnumber ms1_800_000Inactivity before new session.
ignoreUrls(string|RegExp)[][]Drop events whose url matches.
ignoreSelectorsstring[][]Selectors auto-track skips.
globalContextobject{}Static props merged into every event.
pluginsPlugin[][]Plugins registered at init.
enabledbooleantrueMaster kill-switch (opt-out flows).
fetchImpltypeof fetchglobalThis.fetchOverride fetch (SSR / tests).

★ required

Step 04

API reference

The public surface of the Observe singleton.

Observe.track(name, properties?)

Record a product event. Use "Object Action" title-case names.

checkout.ts
Observe.track("Checkout Completed", {
  orderId: order.id,
  total: order.total,
  currency: "USD",
});

Observe.page(properties?)

Record a pageview. Auto-captures URL, path, referrer, and sticky UTM params.

Observe.identify(userId, traits?)

Bind the anonymous visitor to a known user. Traits persist across sessions.

auth.ts
Observe.identify(user.id, {
  email: user.email,
  plan: user.plan,
  createdAt: user.createdAt,
});

Observe.alias, group, setUserProperties, reset

identity.ts
// Merge an anonymous visitor into a user
Observe.alias("user_123");

// Associate a user with an account/org
Observe.group("acme_corp", { plan: "enterprise" });

// Update traits without re-identifying
Observe.setUserProperties({ plan: "enterprise" });

// Clear identity on logout
Observe.reset();

Observe.flush()

Force-flush the queue. Returns a Promise. Useful before redirects or logout.

checkout.ts
await Observe.flush();
window.location.href = "https://billing.stripe.com/...";

captureException, captureMessage, setGlobalContext, shutdown

misc.ts
Observe.captureException(err, { component: "Checkout" });
Observe.captureMessage("Payment retried", "warn");
Observe.setGlobalContext({ tenant: "acme" });
await Observe.shutdown();
Step 05

Framework recipes

Next.js (App Router)

app/layout.tsx
import { ObserveProvider } from "@observejs/nextjs";

export default function RootLayout({ children }) {
  return (
    <ObserveProvider apiKey={process.env.NEXT_PUBLIC_OBSERVE_API_KEY!}>
      {children}
    </ObserveProvider>
  );
}

React / Vite

main.tsx
import { ObserveProvider } from "@observejs/react";

<ObserveProvider apiKey={import.meta.env.VITE_OBSERVE_KEY}>
  <App />
</ObserveProvider>

Vue 3

main.ts
import { ObservePlugin } from "@observejs/vue";

app.use(ObservePlugin, { apiKey: "obs_pk_live_xxx" });

TanStack Router

hooks/use-pageviews.ts
const { location } = useRouterState();
useEffect(() => Observe.page({ path: location.pathname }), [location.pathname]);
Step 06

Event schema

Every event shipped by the SDK carries this shape:

event.json
{
  eventId: "uuid",
  eventName: "Signup Completed",
  type: "track",
  timestamp: "2026-06-27T12:34:56.789Z",
  sessionId, visitorId, deviceId, userId,
  projectKey, environment, release,
  url, path, referrer,
  utm: { source, medium, campaign, term, content },
  device: {
    browser, browserVersion, os, osVersion,
    device: "desktop" | "mobile" | "tablet",
    screen: { width, height, dpr },
    language, timezone, userAgent,
  },
  level: "info" | "warn" | "error",
  metadata: { ...globalContext },
  properties: { ...yourProps },
  sdk: { name, version, api: "v1" }
}
Step 07

Plugins

The SDK is plugin-driven. Auto-track, replay, heatmaps, errors, network, performance, and AI insights are all plugins that share the same contract — no core changes needed.

plugin.ts
import { Observe, definePlugin } from "@observejs/browser";

const consoleSpy = definePlugin({
  name: "console-spy",
  setup(ctx) {
    const off = ctx.on("event", (e) => ctx.logger.debug(e.eventName, e.properties));
    return () => off();
  },
});

Observe.init({ apiKey: "obs_pk_live_xxx", plugins: [consoleSpy] });

Available hooks: init, event, beforeSend, page, identify, flush, shutdown. A throwing plugin is isolated — it can't crash the host app or the SDK.

Official plugins

  • @observejs/plugin-auto-track — clicks, forms, scroll, visibility
  • @observejs/plugin-errors — window errors + unhandled rejections
  • @observejs/plugin-network — fetch + XHR instrumentation
  • @observejs/plugin-performance — Core Web Vitals
  • @observejs/plugin-replay — session replay
  • @observejs/plugin-heatmaps — click / scroll heatmaps
  • @observejs/plugin-ai — AI-powered insights
Step 08

Reliability & privacy

  • Batching. Flushes when batchSize is reached, every flushInterval, on visibilitychange→hidden, and on pagehide.
  • Retries. Exponential backoff (500ms → 30s, 5 attempts) for network / 408 / 429 / 5xx. Permanent 4xx drops the batch.
  • Offline. Queue mirrored to localStorage (capped 500 events), restored on next load.
  • Beacon. Final flush during unload uses navigator.sendBeacon.
  • Safety. Every public method is wrapped — internal errors never reach the host app.
  • Privacy. No third-party cookies. IDs are first-party in localStorage. Use enabled: false for opt-out flows.

Start seeing what your users
actually experience.

Install in under two minutes · no credit card required