JavaScript SDK
Complete reference for the Vendo web tracking JavaScript SDK — all methods, configuration options, and advanced features.
Last reviewed July 25, 2026
The Vendo JavaScript SDK captures events from your website and sends them to your ingestion API. It handles batching, retries, session management, attribution tracking, and identity resolution automatically.
Installation
CDN Snippet (Recommended)
See Quickstart for the full snippet. After pasting it into your <head>, all methods are available on window.vendo.
Self-Hosted Snippet
If you prefer serving the SDK from your own domain, use the self-hosted async snippet. See Self-Hosting for setup instructions.
Direct Script
<script src="https://cdn.vendodata.com/sdk/v1/vendo.js"></script>
<script>
const tracker = VendoTracker.init('YOUR_WRITE_KEY', {
endpoint: 'https://YOUR_TRACKING_ENDPOINT/collect',
});
tracker.page(); // page views are explicit — init() never sends one
</script>Initialization
vendo('init', writeKey, options)
Initialize the SDK using the CDN snippet API. This is the recommended approach.
vendo('init', 'YOUR_WRITE_KEY', {
host: 'https://YOUR_TRACKING_ENDPOINT',
});
vendo('page'); // page views are explicit — init never sends oneThe CDN snippet buffers all calls as arguments. When the SDK loads, autoboot() scans the queue for the init call, initializes the tracker, and replays any remaining buffered calls.
Explicit page views (0.2.0) —
initno longer sends aPage Viewedevent automatically. Callvendo('page')(function-queue form) orvendo.page()(async/npm form) after init, and again on every client-side route change. The oldtrackPageViews/pageViewoptions are deprecated no-ops.
The vendo('init', ...) call accepts these options:
| Option | Type | Default | Description |
|---|---|---|---|
host | string | "" | Base URL of your tracking endpoint. The SDK derives the collect endpoint as {host}/collect. |
debug | boolean | false | Log event payloads, queue changes, batch sends, sendBeacon fallback, and flush results to the browser console. |
trackPageViews | boolean | — | Deprecated no-op since 0.2.0. Has no effect; call vendo('page') explicitly. Alias for pageView. |
pageView | boolean | — | Deprecated no-op since 0.2.0. Has no effect; call vendo('page') explicitly. |
consent | object | {} | Consent configuration: groups, waitForConsent, requiredGroups. See Consent Management. |
flushIntervalMs | number | 5000 | How often (ms) to flush the event queue. |
batchSize | number | 20 | Max events per batch request. |
maxQueueSize | number | 200 | Max events to buffer locally before dropping oldest. |
sessionTimeoutMs | number | 1800000 | Session timeout in ms (default: 30 minutes). A new session starts after this period of inactivity. |
exitIntent | boolean | true | Flush pending events when the page is hidden or unloaded; does not create an exit-intent event. |
storageKey | string | "vendo" | localStorage key prefix for SDK data. |
requestTimeoutMs | number | 30000 | Timeout for each HTTP request to /collect. |
VendoTracker.init(writeKey, config)
Initialize the SDK using the direct API. Returns a tracker instance.
const tracker = VendoTracker.init('YOUR_WRITE_KEY', {
endpoint: 'https://track.yourdomain.com/collect',
debug: true,
});
tracker.page(); // page views are explicit — init() never sends one| Option | Type | Default | Description |
|---|---|---|---|
endpoint | string | "/collect" | Full URL of the collect endpoint. |
All other options are the same as vendo('init', ...).
Tracking Methods
track(event, properties, context)
Track a custom event.
vendo.track('Product Viewed', {
sku: 'SKU-1',
price: 29.99,
category: 'shoes',
currency: 'USD',
});
vendo.track('Product Viewed', { sku: 'SKU-1' }, { page: { category: 'sale' } });| Parameter | Type | Required | Description |
|---|---|---|---|
event | string | Yes | Name of the event (e.g., "Product Viewed", "Signup Completed"). |
properties | object | No | Key-value pairs of event properties. |
context | object | No | Additional context to merge into the SDK’s automatic context envelope. |
Events are queued locally and sent in the next batch flush. The SDK automatically stamps every event with these top-level fields — they sit alongside event and properties on the event object, not inside properties:
| Field | Description |
|---|---|
messageId | Unique ID (UUID v4) |
type | Event type (track, page, …) |
timestamp | ISO 8601 timestamp |
anonymousId | Persistent anonymous ID |
userId | Set after identify() is called |
sessionId | Current session ID |
The write key is not a per-event field. It is sent once per batch — in the X-Write-Key header and as a batch-level writeKey on the request wrapper — not repeated on each event.
Everything else the SDK captures automatically (browser, OS, device, locale, page, screen, library, session, campaign/UTMs, click IDs, and consent) lives in the structured context object described below. UTM parameters and click IDs are populated when present (see Attribution).
Context Envelope
Every event also includes a structured context object. This is the preferred shape for shared metadata because it keeps event properties focused on the business event.
{
"context": {
"source": "web",
"library": { "name": "vendo-web-tracking", "version": "0.2.0" },
"session": { "id": "session-id" },
"page": {
"url": "https://example.com/products/sku-1?utm_source=google",
"path": "/products/sku-1",
"title": "Product Page",
"referrer": "https://google.com/"
},
"campaign": { "utm_source": "google", "utm_medium": "cpc" },
"clickId": { "gclid": "..." },
"screen": { "width": 1440, "height": 900 },
"browser": "Chrome",
"os": "Mac OS X",
"device": "Desktop",
"language": "en-US",
"consent": { "analytics": true, "marketing": false }
}
}The ingestion API normalizes this context into typed warehouse columns and still falls back to the legacy flat properties for backwards compatibility.
page(name, properties, context)
Track a page view. Automatically captures page URL, path, title, and referrer.
// Basic page view
vendo.page();
// Named page view with custom properties
vendo.page('Pricing', { variant: 'annual' });Page views are always explicit: init never sends one automatically (since 0.2.0), so call page() once after init and on every client-side route change.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | No | Page name. Defaults to "Page Viewed". |
properties | object | No | Additional page properties. |
context | object | No | Additional context to merge into the automatic context envelope. |
Page events are written to the events table with type: "page".
identify(userId, traits, context)
Associate the current visitor with a known user ID and set user traits.
vendo.identify('user-123', {
email: 'jane@example.com',
name: 'Jane Smith',
plan: 'pro',
created_at: '2024-01-15T00:00:00Z',
});| Parameter | Type | Required | Description |
|---|---|---|---|
userId | string | Yes | Unique user identifier from your system. |
traits | object | No | User properties (email, name, plan, etc.). |
context | object | No | Additional context to attach to the identify call. |
After calling identify():
- The
userIdis persisted in localStorage and attached to all subsequent events - An identify event is sent to the
userstable in BigQuery - The anonymous ID is preserved, allowing you to link pre-login and post-login activity
Call identify() after login, signup, or whenever user info changes.
group(groupId, traits, context)
Associate the current user with a group (company, organization, account).
vendo.group('org-42', {
name: 'Acme Inc',
plan: 'Enterprise',
employee_count: 150,
});| Parameter | Type | Required | Description |
|---|---|---|---|
groupId | string | Yes | Unique group identifier. |
traits | object | No | Group properties. |
context | object | No | Additional context to attach to the group call. |
Group events are written to the groups table in BigQuery.
alias(userId, previousId, context)
Merge two user identities. Use this when a user creates an account and you want to link their anonymous activity to the new account.
vendo.alias('user-123', 'anon-456');| Parameter | Type | Required | Description |
|---|---|---|---|
userId | string | Yes | The new canonical user ID. |
previousId | string | Yes | The previous anonymous or temporary ID. |
context | object | No | Additional context to attach to the alias call. |
Alias events are written to the aliases table in BigQuery.
Queue Management
flush()
Immediately send all queued events. Useful before a user navigates away.
vendo.flush();The SDK flushes automatically on the configured interval (flushIntervalMs, default 5 seconds) and when the batch size is reached. You rarely need to call this manually.
debugState()
Return the current SDK state without sending an event. Use this while validating an implementation.
window.vendo.debugState();Example response:
{
endpoint: "https://track.yourdomain.com/collect",
storageKey: "vendo",
anonymousId: "anon-id",
userId: "user-123",
sessionId: "session-id",
queueLength: 0,
optOut: false,
consent: {},
writeKeyPresent: true,
writeKeySuffix: "abcd",
config: {
batchSize: 20,
flushIntervalMs: 5000,
maxQueueSize: 200
}
}reset()
Clear all stored user data — anonymous ID, user ID, session, attribution, and the event queue. Call this on logout.
vendo.reset();After calling reset():
- A new anonymous ID is generated on the next event
- User ID is cleared
- Session is reset
- Attribution data (UTMs, click IDs) is cleared
- Event queue is cleared
Privacy
optOut()
Opt the user out of tracking. No events are collected or sent.
vendo.optOut();optOut() immediately purges any queued events (including the persisted localStorage queue) and writes a persistent opt-out flag, so the opt-out survives page reloads: on the next page load the SDK starts opted out and neither replays nor resumes tracking.
optIn()
Opt the user back in after an optOut() call. Clears the persisted opt-out flag and re-enables tracking.
vendo.optIn();Use these for GDPR/CCPA compliance. When opted out, all tracking methods become no-ops.
setConsent(groups)
Update the visitor’s consent state. Pass an object of consent group names to booleans; the values are merged into the current consent state and attached to every subsequent event at context.consent.
vendo.setConsent({ analytics: true, marketing: false });If the SDK was initialized with waitForConsent: true, granting one of the required consent groups here (consent.requiredGroups, default ["analytics"]) flips the SDK out of its holding state and flushes the events that were queued while it waited. A decision that grants none of the required groups (e.g. “Reject all”, or necessary: true alone) resolves as denied and purges the held events. Without waitForConsent: true, consent values are stamped onto events but never gate sending.
getConsent()
Return a copy of the current consent groups.
vendo.getConsent(); // → { analytics: true, marketing: false }For a full walkthrough of consent, waitForConsent, and automatic detection of consent-management platforms (OneTrust, Usercentrics, CookiePro, CookieFirst), see Consent Management.
Identity and Session Helpers
getAnonymousId()
Return the current persistent anonymous ID.
vendo.getAnonymousId(); // → "anon-abc-123"getSessionId()
Return the current session ID.
vendo.getSessionId(); // → "session-abc-123"destroy()
Tear down the tracker instance. Removes the flush timer, consent-management-platform listeners, declarative-tracking listeners, and the pagehide / beforeunload / visibilitychange window listeners.
vendo.destroy();destroy() does not clear stored data — anonymous ID, user ID, session, and attribution remain in localStorage. Use reset() when you want to clear stored data (e.g., on logout).
Attribution Tracking
The SDK automatically captures UTM parameters and advertising click IDs from the URL on page load.
UTM Parameters
| Parameter | Example |
|---|---|
utm_source | google |
utm_medium | cpc |
utm_campaign | spring_sale_2026 |
utm_content | banner_a |
utm_term | analytics tool |
utm_id | camp_12345 |
utm_source_platform | google |
utm_campaign_id | 12345678 |
utm_creative_format | image |
utm_marketing_tactic | prospecting |
Click IDs
| Parameter | Platform |
|---|---|
gclid | Google Ads |
fbclid | Meta Ads |
msclkid | Microsoft Ads |
ttclid | TikTok Ads |
sccid | Snapchat Ads |
dclid | Google Display |
twclid | Twitter/X |
wbraid | Google Web-to-App |
gbraid | Google App-to-Web |
gad_source | Google Ads |
li_fat_id | |
epik | |
ko_click_id | Kochava |
aleid | AliExpress |
First-Touch vs Last-Touch
The SDK stores both first-touch and last-touch values:
- First-touch (
utm_source_first,gclid_first, etc.) — Set once on the user’s first visit. Never overwritten. - Last-touch (
utm_source_last,gclid_last, etc.) — Updated on every visit with new URL parameters.
Both are attached to every event as properties.
Session Management
Sessions are tracked automatically using an inactivity timeout (default: 30 minutes).
- A session ID is generated on first activity
- The session remains active as long as events occur within the timeout window
- After the timeout, the next event starts a new session
- Session ID is attached to every event as
sessionId
Configure the timeout at initialization:
vendo('init', 'YOUR_WRITE_KEY', {
host: 'https://track.yourdomain.com',
sessionTimeoutMs: 3600000, // 1 hour
});Local Storage
The SDK uses localStorage with the configured prefix (default: vendo) to persist:
| Key | Contents |
|---|---|
vendo | Anonymous ID, user ID, session, UTM parameters, click IDs |
vendo_queue | Pending events waiting to be sent |
All data is cleared when reset() is called.
Debug Mode
Enable debug mode to log all SDK activity to the browser console:
vendo('init', 'YOUR_WRITE_KEY', {
host: 'https://track.yourdomain.com',
debug: true,
});Debug output includes:
- Every tracked event with its full payload
- Queue changes, batch flush attempts, responses, and flush failures
sendBeaconfallback attempts during page unload- Session rotation events
- Attribution parameter capture
Use window.vendo.debugState() alongside console logs to confirm the active endpoint, queue length, session ID, and consent state.
Remove debug: true before going to production.
Error Handling and Retries
Failed requests are retried automatically with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | ~1 second |
| 2nd retry | ~2 seconds |
| 3rd retry | ~4 seconds |
Events remain in the queue across page loads (persisted in localStorage). If the queue exceeds maxQueueSize (default: 200), the oldest events are dropped.
React SPA (Vite / Create React App)
Add the CDN snippet to your index.html:
<!-- index.html -->
<head>
<script>
(function (w, d, s, e, l, k) {
w['VendoObject'] = e;
w[e] =
w[e] ||
function () {
(w[e].q = w[e].q || []).push(arguments);
};
w[e].l = 1 * new Date();
l = d.createElement(s);
k = d.getElementsByTagName(s)[0];
l.async = 1;
l.src = 'https://cdn.vendodata.com/sdk/v1/vendo.js';
k.parentNode.insertBefore(l, k);
})(window, document, 'script', 'vendo');
vendo('init', 'YOUR_WRITE_KEY', { host: 'https://YOUR_TRACKING_ENDPOINT' });
vendo('page');
</script>
</head>For React Router, track route changes with a component. It fires on mount, so it also covers the initial page view — if you use it, remove the vendo('page') call from the head snippet to avoid a duplicate:
// components/TrackPageViews.tsx
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
declare global {
interface Window {
vendo?: (...args: unknown[]) => void;
}
}
export function TrackPageViews() {
const location = useLocation();
useEffect(() => {
if (typeof window.vendo === 'function') {
window.vendo('page');
}
}, [location]);
return null;
}Add it inside your <BrowserRouter>:
<BrowserRouter>
<TrackPageViews />
<Routes>{/* your routes */}</Routes>
</BrowserRouter>Framework Guides
For framework-specific setup (route-change tracking, plugins, etc.), see the dedicated guides:
- Next.js — App Router and Pages Router
- Vue — Vue 3 plugin with Vue Router
- Nuxt — Nuxt 3 client plugin
Related
- Quickstart — Get started in 5 minutes
- API Reference —
/collectendpoint and event schema - Self-Hosting — Deploy the ingestion API