Official SDKs

Javascript SDK

What this is: a tiny, privacy-friendly client library that tracks page views and conversions without cookies. What you get: anonymous visit counts, referrers, and optional conversion goals/amounts. What it won’t do: fingerprint, store cookies, or send PII.


Quick start

Add the loader script

Replace <WORKSPACE_ID> with your own.

<script>
  (function () {
    window.scoby = window.scoby || function () {
      (window.scoby.q = window.scoby.q || []).push(arguments);
    };
    // Optional: turn off autologging if you want to call logPageView yourself
    window.scoby('init', { autoLogging: true });
  })();
</script>
<script async src="https://<WORKSPACE_ID>.s3y.io"></script>

Verify it works

Open your site → devtools → Network. You should see requests to https://<WORKSPACE_ID>.s3y.io/count after a page load and on navigation (for SPAs). If you’ve set a conversion (below), you’ll see .../count?ev=conversion....


API reference

scoby('init', settings?)

Configure the client. Safe to call multiple times; later calls merge settings.

scoby('init', {
  autoLogging: true,         // automatically logs page views + SPA navigations
  // other settings may be added over time
});

scoby('logPageView', payload?)

Manually log a page view. Useful when autoLogging is false, or for custom SPA navigation hooks.

scoby('logPageView', {
  url: location.href,        // optional; default: current URL
  referrer: document.referrer, // optional
  segments: ['paid', 'eu']   // optional; array of short tags
});

scoby('logConversion', payload)

Log a conversion (goal completion). goal is required.

scoby('logConversion', {
  goal: 'Purchase',          // required (short, human-readable)
  amount: 129.99,            // optional number
  currency: 'EUR'            // optional ISO code, e.g., 'USD', 'EUR'
});

Idempotency guard: The client de-duplicates page views by URL after cleaning (see URL cleaning). If nothing changed, it won’t send again.


How it works (in one minute)

  • The inline snippet creates a command queue (window.scoby.q) so you can call scoby(...) before the network script loads.

  • When the script loads, it replaces the shim with the real dispatcher, drains queued commands, and starts tracking.

  • If autoLogging: true:

    • Logs an initial page view.
    • Hooks into popstate, hashchange, and pageshow (bfcache) for SPA navigation.
  • Page views are sent to https://<WORKSPACE_ID>.s3y.io/count with url, optional ref (referrer origin), and optional sg (segments).

  • Conversions are sent to the same endpoint with ev=conversion, gl (goal), optional amt/cur, and the cleaned url.


URL cleaning (UTM stripping / allow-list)

To avoid inflating metrics with tracking params, the client “cleans” URLs before sending them.

  • Keep only allow-listed query params ({{{WHITELISTED_URL_PARAMS}}} in your build).
  • Remove everything else (typical: utm_*, gclid, fbclid, etc.).
  • If the cleaned URL equals the last sent URL, skip sending (prevents duplicates on SPA re-renders).

Note: make sure your allow-list check treats first index as found (i.e., indexOf(k) >= 0, not > 0). Otherwise the first allow-listed key may be skipped.


Patterns & examples

SPA frameworks (manual navigation hooks)

When autoLogging is false (you want full control):

scoby('init', { autoLogging: false });

// Log first view
scoby('logPageView');

// Example: Next.js (app router)
import { usePathname, useSearchParams } from 'next/navigation';
useEffect(() => {
  scoby('logPageView');
}, [usePathname(), useSearchParams()]);

// Example: React Router
import { useLocation } from 'react-router-dom';
const { pathname, search } = useLocation();
useEffect(() => {
  scoby('logPageView');
}, [pathname, search]);

Recording segments (cohorts/tags)

// On login or when you know a visitor belongs to a cohort:
scoby('logPageView', { segments: ['member'] });

// Or pass segments with conversions:
scoby('logConversion', { goal: 'Signup', segments: ['beta'] });

Segments are short, non-identifying tags that help you break down reports (e.g., ['paid', 'trial', 'nl']). Avoid PII.

Logging conversions

Checkout thank-you page:

scoby('logConversion', { goal: 'Purchase', amount: 49, currency: 'EUR' });

Lead form success:

scoby('logConversion', { goal: 'Lead' });

Multiple steps: log each milestone with a distinct goal name ('Signup Step 1', 'Signup Completed').


  • The client does not use cookies and sends no PII by default.
  • Requests use mode: "no-cors", credentials: "omit", referrerPolicy: "no-referrer".
  • Under EU ePrivacy/GDPR, basic page-view & conversion counts typically don’t require consent. If you add custom data, ensure it remains non-identifying and documented.

Configuration options (current)

OptionTypeDefaultDescription
autoLoggingbooleantrueIf true, logs initial page view and SPA navigations (popstate, hashchange, pageshow(bfcache)). Set to false if you’ll call logPageView yourself after your router updates.

(The configuration may expand; additional keys will be merged at runtime.)


Transport details

  • Endpoint: https://<WORKSPACE_ID>.s3y.io/count
  • Method: GET with query string
  • Cache busting: a random query key is appended per request
  • CORS mode: no-cors (intentionally opaque—network tab shows the request, but JS can’t read the response)

Troubleshooting

I don’t see any requests.

  • Ensure the network script URL is correct: https://<WORKSPACE_ID>.s3y.io loads without blockers.
  • Ad blockers may hide requests—try a clean profile or private window.

Only the first page view logs in my SPA.

  • With autoLogging: true, the library listens to popstate/hashchange. If your router doesn’t trigger these (e.g., some custom history setups), either call scoby('logPageView') on route changes, or set autoLogging: false and wire it manually.

Conversions aren’t recorded.

  • goal is required ({ goal: 'Purchase' }).
  • Make sure you call it on the final state (thank-you page or confirmed event), not mid-checkout.
  • Check for console warnings.

Referrer is missing.

  • Many browsers drop cross-site referrers with strict policies; the client sends only the origin (e.g., https://example.com) when available.

My analytics show too many entries for the same page.

  • Verify your URL cleaning allow-list and the duplicate guard. If the allow-list differs per page, the “cleaned” URL might oscillate. Keep it consistent.

Security & performance notes

  • The script is small and async; it won’t block rendering.
  • No cookies, no localStorage, no cross-site credentials.
  • Requests are GET and cache-busted; they won’t be served from intermediate caches.

Example: minimal inline loader (queue + init)

Use this if you prefer the smallest possible inline snippet:

<script>
  (function () {
    window.scoby = window.scoby || function () {
      (window.scoby.q = window.scoby.q || []).push(arguments);
    };
    window.scoby('init', { autoLogging: true });
  })();
</script>
<script async src="https://<WORKSPACE_ID>.s3y.io"></script>

Changelog & versioning

We keep the client stable and backwards-compatible. New config keys are additive. Breaking changes—if ever necessary—will be versioned on a new script path and documented here.


Need help?

Spotted something off or have an integration we should document? Ping us at hello@scoby.io—thank you for helping keep these docs sharp!

Previous
Shopware

Please note:Scoby does not provide legal advice. The information provided in this documentation is for general informational purposes only and does not constitute legal consultation. You should always consult your legal counsel or Data Protection Officer (DPO) to assess how applicable laws and regulations apply to your specific situation. If your DPO or legal team has questions about Scoby Analytics, they can contact us. We’re happy to provide detailed technical explanations.