Atribu
SDKs

@atribu/tracker

Official browser tracking SDK — page views, sessions, engagement, outbound links, file downloads, forms, bookings, all auto-captured.

@atribu/tracker is a npm-installable alternative to the <script src="atribu-tracker.js"> tag — same runtime, bundled inline, configured at init() time. Most teams using a modern frontend framework (React, Next.js, Vue, Svelte) will prefer this over the script tag.

Install

npm install @atribu/tracker

Quick start

import { init } from "@atribu/tracker";

init({
  trackingKey: "trk_live_...", // from Tracking Settings → Ingest Keys
});

That's it. Page views, sessions, engagement, outbound links, file downloads, form submissions, and booking detection are all auto-captured.

What's auto-captured

SignalTriggers
Page viewsinit, SPA navigation (pushState/replaceState/popstate/hashchange), bfcache restoration
Sessions30 min inactivity timeout (configurable 1–120 min)
EngagementScroll depth (px + %), time on page — sent on page exit
Outbound linksExternal link clicks via sendBeacon
File downloadspdf · xlsx · docx · csv · zip · mp4 · and 30+ more
Form submissionsStandard <form> submits AND GHL-style <div> form fetches
Booking detectionCalendly · Cal.com · GoHighLevel · custom postMessage patterns
Meta Pixel bridgeIntercepts fbq() calls for server-side CAPI dedup
Bot filteringBlocks bots; tags AI agents (ChatGPT, Claude, Perplexity, Gemini)
Declarative eventsdata-atribu-event HTML attributes — no-code event tracking

Configuration

init({
  trackingKey: "trk_live_...",                  // required
  apiHost: "https://tracking.example.com",      // custom tracking domain (optional)
  trackingEndpoint: "...",                       // full override of collect URL (optional)
  interceptMetaFbq: true,                       // mirror Meta Pixel events (default true)
  metaBridgePageview: false,                    // mirror Meta PageView too (default false)
  customProperties: { tier: "pro" },            // static props on every event
  transformRequest: (event) => event,           // event middleware (return null to suppress)
  ignoredPages: ["/admin/*"],                   // URL patterns with `*` wildcards
  sessionTimeoutMinutes: 30,                    // 1–120 min
  sessionMode: "inactivity_only",               // or "inactivity_or_source_change"
});

Framework setup

app/providers.tsx
"use client";

import { useEffect } from "react";
import { init } from "@atribu/tracker";

export function AtribuProvider({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    init({ trackingKey: "trk_live_..." });
  }, []);
  return <>{children}</>;
}
app/layout.tsx
import { AtribuProvider } from "./providers";

export default function RootLayout({ children }) {
  return (
    <html><body><AtribuProvider>{children}</AtribuProvider></body></html>
  );
}
import { useEffect } from "react";
import { init } from "@atribu/tracker";

function App() {
  useEffect(() => {
    init({ trackingKey: "trk_live_..." });
  }, []);
  return <>{/* your app */}</>;
}
import { onMounted } from "vue";
import { init } from "@atribu/tracker";

onMounted(() => init({ trackingKey: "trk_live_..." }));
import { onMount } from "svelte";
import { init } from "@atribu/tracker";

onMount(() => init({ trackingKey: "trk_live_..." }));
<script type="module">
  import { init, track } from "@atribu/tracker";

  init({ trackingKey: "trk_live_..." });

  document.querySelector("#signup-btn").addEventListener("click", () => {
    track("signup", { source: "landing" });
  });
</script>

Custom events

import { track, trackRevenue } from "@atribu/tracker";

track("button_click", { label: "hero-cta" });

trackRevenue("purchase", 99.99, "USD", { plan: "enterprise" });

User identification

Two complementary APIs:

import { identify, setUserId } from "@atribu/tracker";

// PII-based — joins this visitor to the customer_profile via identity graph
identify({
  email: "[email protected]",
  firstName: "Jane",
  lastName: "Doe",
  phone: "+1234567890",
});

// Stable user_id stamped on every future event (e.g. on login)
setUserId("user_abc123");

// Clear on logout
setUserId(null);

identify does the heavier identity-graph work (anonymous_id ↔ email ↔ phone resolution). setUserId just stamps your application's user ID on events.

Attribution passthrough (checkout)

When a customer pays through Stripe / MercadoPago, the payment carries no ad signal — so Atribu has to guess which visit it belongs to by matching email. Hand the payment the exact identity + ad signal the tracker already holds and the guess becomes a direct match, so the sale ties to the precise ad that drove it.

getAttribution() returns the fields; getAttributionToken() packs them into one compact string you can drop into a hidden form field, a Stripe metadata value, or a client_reference_id.

import { getAttribution, getAttributionToken } from "@atribu/tracker";

getAttribution();
// {
//   anonymous_id: "anon_…", session_id: "sess_…",
//   utm_source: "facebook", utm_campaign: "1202…", utm_content: "1202…",
//   fbclid: "IwAR…", gclid: "Cj0…",
//   first_touch: { utm: {…}, click_ids: {…}, captured_at: "…" }
// }

getAttributionToken(); // "atb1.eyJhbm9ueW1vdXNfaWQiOiJhbm9uXy4uLiJ9…"

Carry the token into the checkout. Easiest is a hidden field on the form that starts checkout:

<input type="hidden" name="atribu_attribution" id="atribu_attribution" />
<script>
  document.getElementById("atribu_attribution").value =
    window.atribuTracker.getAttributionToken();
</script>

Or attach it to the Stripe payment you create server-side. Put the token on metadata.atribu_attribution — on the Checkout Session:

// client → send both with the checkout request
const attribution = window.atribuTracker.getAttribution();
const token = window.atribuTracker.getAttributionToken();

// server → Stripe Checkout Session
await stripe.checkout.sessions.create({
  // …line_items, mode, success_url…
  client_reference_id: attribution.anonymous_id, // ≤200 chars — id only
  metadata: { atribu_attribution: token },        // ≤500 chars — the full token
});

…or directly on a PaymentIntent if you build your own payment flow:

await stripe.paymentIntents.create({
  amount, currency,
  metadata: { atribu_attribution: token },        // read back on charge.succeeded
});

Stripe Payment Links are zero-config. Any buy.stripe.com Payment Link on a page running the tracker is auto-decorated with ?client_reference_id={anonymous_id} at click time (an existing client_reference_id is never overwritten), and Atribu-generated Payment Links already carry the customer in metadata — both attribute with no code from you.

Atribu reads it automatically. If the paying Stripe account is connected to Atribu, our payment webhook decodes metadata.atribu_attribution (or client_reference_id), stamps the visitor's anonymous_id / session / click-ids onto the cash event, and links it to the exact ad-click session on the next recompute — no email or phone match required. You only have to put the token in metadata (or use a Payment Link on a tracked page).

Processing the payment yourself? Decode it with the same server helper and hand the fields to your ingestion:

import { parseAttributionToken } from "@atribu/analytics-enrichment/attribution-token";

const attribution = parseAttributionToken(session.metadata?.atribu_attribution);
// → { anonymous_id, session_id, utm_*, fbclid, gclid, first_touch } | null

parseAttributionToken returns null for a missing/garbled token (fall back to email matching) and ignores any field that isn't part of the contract.

MercadoPago works the same way — put the token on the preference metadata.atribu_attribution or external_reference when you create the checkout, and Atribu's MP webhook reads it back (no code if the MP account is connected to Atribu). Atribu also recovers the customer name + phone that Shopify-via-MP hides in the payment metadata.

Reading before the tracker loads. getAttribution() is synchronous once the tracker is on the page. If your checkout script may run first, queue a ready callback on the loader stub — it fires with the live tracker:

<script>
  window.atribuTracker = window.atribuTracker || { q: [] };
  window.atribuTracker.q.push(["ready", function (t) {
    document.getElementById("atribu_attribution").value = t.getAttributionToken();
  }]);
</script>

Once loaded, atribuTracker.ready(cb) and await atribuTracker.getAttributionAsync() are the post-init equivalents.

Confirmation-page purchase

Fire a purchase on your thank-you / order-confirmation page. Because it runs on the same device with the live anonymous_id/session_id, the sale is captured with its ad-click lineage — a same-device backstop for when the payment provider can't carry the attribution token.

import { purchase } from "@atribu/tracker";

// on the confirmation page, once the order is confirmed:
purchase({ value: 149.0, currency: "USD", orderId: "ord_1234" });

Or with the script-tag global: window.atribuTracker.purchase({ value, currency, orderId }).

Pass your own orderId — it's the key that ties this confirmation to the payment your provider reports. This event does not replace the provider's payment record (that's what carries the cash); it makes sure the ad-click session is captured on the paying device so the payment stitches to the exact ad. Pair it with the checkout token above (or an identify() call) for the strongest link.

import { setConsent } from "@atribu/tracker";

setConsent({ analytics: true, marketing: false });

Consent state persists to localStorage and is attached to every event. Events fired before consent are queued and replayed once granted.

Declarative events (no-code)

Track events without writing JavaScript using HTML data attributes:

<button
  data-atribu-event="signup"
  data-atribu-prop-plan="pro"
  data-atribu-revenue="99.99"
  data-atribu-currency="USD"
>
  Sign Up
</button>

The tracker auto-captures clicks on elements with data-atribu-event and extracts:

  • Event name from data-atribu-event
  • Custom properties from data-atribu-prop-* attributes
  • Revenue from data-atribu-revenue + data-atribu-currency

Privacy

What the tracker does NOT do

  • No third-party tracking — all data goes to your configured endpoint only
  • No cookies set for tracking IDs (visitor + session IDs live in localStorage)
  • No fingerprinting — visitor IDs are cryptographically random, not derived from device characteristics
  • First-party only. All data goes to your configured endpoint. No cross-site identifiers.
  • No cookies set for tracking. Visitor + session IDs live in localStorage. The tracker reads _fbp/_fbc cookies (set by Meta Pixel) only when present, for CAPI match quality.
  • Consent-aware. setConsent() state is persisted and attached to every event.
  • Bot filtering on-device. Traditional bots are blocked at the client level. AI agents (ChatGPT, Claude, Perplexity, Gemini) are tracked but tagged visitor_type: "ai_agent" so you can filter them in reporting.

Lifecycle controls

import { flush, reset } from "@atribu/tracker";

flush(); // force-send queued events (e.g. before navigating away)
reset(); // clear visitor ID, session, all stored state (e.g. on logout)

Singleton access

Initialize once, retrieve from anywhere:

import { init, getTracker } from "@atribu/tracker";

// At app entry:
init({ trackingKey: "trk_live_..." });

// In any other module:
const atribu = getTracker();
atribu.track("button_click", { label: "hero-cta" });

Developer exclusion

Exclude yourself from tracking during development:

localStorage.atribu_ignore = "true";

Remove it to re-enable:

delete localStorage.atribu_ignore;

Runtime support

RuntimeSupported
Modern browsers (Chrome, Safari, Firefox, Edge)
Mobile webviews (iOS, Android)
SSR (Node 18+, Next.js server components)✅ — no-op fallback
Bun (browser bundle)
Deno❌ (no DOM)

In SSR contexts, init() returns a silent no-op client so you can safely import + call from server components.

Provenance

Every published version of @atribu/tracker ships with a Sigstore provenance attestation signed by GitHub Actions OIDC. Verify with:

npm audit signatures

On this page