SocialHub.AI
Developer Center · Web Tracking

Track behavior on your own site.

Drop one first-party beacon on any marketing or e-commerce site and its page views flow into the same member behavior timeline as email, the loyalty portal and your app. No key, no dependency — a few lines of vanilla JS posting to a public endpoint. Flash parses device, geo and bot detection server-side.

01 — How it works

One hosted line, or a self-contained snippet

Add the hosted tracker with a single script tag, or paste the self-contained snippet if you prefer no external dependency — either way it generates the identifiers, respects privacy signals, and POSTs a page-view to one endpoint:

POST https://flash.socialhub.ai/api/v1/tracking/pageview

You send the client bits

URL, title, referrer, ids, screen, language, timezone, UTM — that is all.

Flash enriches server-side

Device, OS and browser are parsed from the User-Agent; country from the IP; bots are detected and flagged. The client never has to be trusted for these.

It always returns 204

Bot, GPC, malformed, or accepted — the endpoint answers silently. Tracking never throws an error into your page.

On your own domain, behavior is anonymous. There is no Flash session cookie on your site, so page views are keyed to the anonymous_id you generate — not to a known member. Member attribution happens on Flash-owned surfaces (the loyalty portal and the in-app member center), where the session resolves the member. Use a consistent anonymous_idso a visitor's on-site behavior is at least unified across their own sessions.

02 — Install

Two ways in. Set YOUR_TEAM_IDand you're live.

Recommended

Option A · Hosted — one line

Drop one script tag before </body>. It auto-tracks single-page-app route changes and exposes window.flashTrack() — nothing else to wire up.

index.html
<!-- SocialHub web tracking — one line, before </body> -->
<script src="https://flash.socialhub.ai/sdk/flash-tracker.js"
        data-team-id="YOUR_TEAM_ID" defer></script>
↓ Download flash-tracker.jsView source ↗no dependencies · self-host from your own origin if you prefer

Option B · Inline — no external script

Want zero external requests (strict CSP, or the code inside your own bundle)? Paste this self-contained snippet instead. It exposes window.shTrack() — call it after each route change in a single-page app:

index.html
<!-- SocialHub web tracking — paste before </body> on every page -->
<script>
(function () {
  var TEAM_ID = "YOUR_TEAM_ID";                                  // Settings → API
  var ENDPOINT = "https://flash.socialhub.ai/api/v1/tracking/pageview";

  // Honor Global Privacy Control / Do Not Track — no signal sent.
  if (navigator.globalPrivacyControl === true || navigator.doNotTrack === "1") return;

  function uid() {
    return (window.crypto && crypto.randomUUID)
      ? crypto.randomUUID()
      : (Date.now() + "-" + Math.random().toString(16).slice(2));
  }
  function persist(store, key) {
    try { var v = store.getItem(key); if (!v) { v = uid(); store.setItem(key, v); } return v; }
    catch (e) { return uid(); }
  }
  function utm() {
    var p = new URLSearchParams(location.search), o = {};
    ["source", "medium", "campaign", "term", "content"].forEach(function (n) {
      var val = p.get("utm_" + n); if (val) o[n] = val;
    });
    return Object.keys(o).length ? o : undefined;
  }
  function track() {
    var body = JSON.stringify({
      v: 1,
      page_url: location.href,
      page_title: document.title,
      referrer: document.referrer || undefined,
      anonymous_id: persist(localStorage, "sh_anon"),       // stable per browser
      session_id: persist(sessionStorage, "sh_session"),    // resets per tab session
      team_id: TEAM_ID,
      utm: utm(),
      screen: screen.width + "x" + screen.height,
      language: navigator.language,
      timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
      timestamp: Date.now()
    });
    try {
      if (navigator.sendBeacon) navigator.sendBeacon(ENDPOINT, body);
      else fetch(ENDPOINT, { method: "POST", body: body, keepalive: true }).catch(function () {});
    } catch (e) { /* never break the page */ }
  }

  window.shTrack = track;   // for SPAs: call window.shTrack() after each route change
  track();                  // initial page view
})();
</script>
spa-router.js
// Single-page apps: after each client-side route change
router.afterEach(function () { window.shTrack && window.shTrack(); });
03 — Payload contract

The beacon body

Send application/json (or a plain string via sendBeacon). Anything over 4 KB or failing validation is dropped silently.

FieldTypeNotes
vnumberSchema version. Always 1.
page_urlstringFull URL of the page (≤ 2048). Required.
page_titlestring?Document title (≤ 500).
referrerstring?document.referrer (≤ 2048).
anonymous_idstringStable per-browser id you generate (≤ 36). Required.
session_idstringPer-session id you generate (≤ 36). Required.
team_idstringYour SocialHub team id (≤ 36) — identifies the brand. Required.
utmobject?{ source, medium, campaign, term, content } parsed from the query string.
screenstring?"1920x1080".
languagestring?navigator.language.
timezonestring?IANA tz, e.g. America/Los_Angeles.
timestampnumberClient epoch ms. Required.
04 — Privacy

Respect the visitor by construction

GPC & Do-Not-Track

The snippet checks navigator.globalPrivacyControl and navigator.doNotTrack and sends nothing when either is set. Keep that guard.

No raw PII

Send identifiers and page context only — never names, emails or form contents in the beacon. Member identity is resolved on Flash surfaces, not on your site.

Your consent banner first

If your site uses a consent manager, gate the snippet behind consent — only call the tracker once the visitor has accepted analytics.

Bot-filtered

Server-side bot detection flags non-human traffic so it does not pollute your member timeline or stats.

Capture the rest of the journey

Web is one of four surfaces. See how email, the loyalty portal and your app feed the same member timeline.