JavaScript SDK
Complete reference for the Vendo web tracking JavaScript SDK: all methods, configuration options, and advanced features.
Last reviewed September 15, 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 SDK File
You can serve the SDK file from your own domain.
- Download
https://cdn.vendodata.com/sdk/v1/vendo.js. - Put the file on your web server, for example at
/js/vendo.js. - In Vendo, open the tracking source.
- In Installation Snippet, click the Self-hosted tab.
- Copy the snippet.
- Replace
https://YOUR_DOMAIN/vendo.jswith the address of your file.
When Vendo releases a new SDK version, download the file again. To run the ingestion API on your own servers, see Self-Hosting.
Known issue: The array-based snippet (
vendo.load(...)) has only these methods before the SDK loads:track,identify,page,group,alias,reset,optOut,optInandflush. It does not havesetConsent,getConsentordebugState. Call these 3 methods only after the SDK loads. The snippet from the Vendo app does not have this limit.
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')after init, and again on every client-side route change. The oldtrackPageViews/pageViewoptions are deprecated no-ops.
Call Form and Method Form
You can call every method in 2 forms:
- The call form, for example
vendo('track', 'Signup Completed'). It works before and after the SDK loads. - The method form, for example
vendo.track('Signup Completed'). It works only after the SDK loads.
Use the call form in code that can run early, for example in the snippet or in a route listener. The SDK is not published as an npm package. Load it with the snippet or from https://cdn.vendodata.com/sdk/v1/vendo.js.
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, 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. It 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. |
maxEventBytes | number | 32000 | Max size of 1 event in bytes (UTF-8 JSON). The SDK drops a larger event. See Size Limits. |
maxBatchBytes | number | 256000 | Max size of 1 batch request in bytes. Events that do not fit wait for the next flush. |
maxAttempts | number | 20 | Max number of sends for 1 event. After this number of failed sends, the SDK drops the event. |
retryBaseMs | number | 1000 | Delay before the first retry, in ms. The delay doubles after each failed send. |
retryMaxMs | number | 300000 | Max delay between retries, in ms (default: 5 minutes). |
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 (for example, "Product Viewed" or "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. |
The SDK queues events locally and sends them in the next batch flush. It automatically adds these top-level fields to every event. They are next to 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. The SDK sends it once per batch, in the X-Write-Key header and as a batch-level writeKey on the request wrapper. It does not repeat the write key 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). The SDK also copies most of these values into properties (or traits) as flat keys, for example page_url and utm_source.
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",
"language": "en-US",
"consent": { "analytics": true, "marketing": false }
}
}The ingestion API normalizes this context into typed warehouse columns. It still falls back to the legacy flat properties for backwards compatibility.
page(name, properties, context)
Track a page view. The SDK automatically captures the 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. Since 0.2.0, init never sends one automatically. 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 (for example, email, name, plan). |
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 queued events. One call sends 1 batch of up to batchSize events. This is useful before a user navigates away.
vendo.flush();The SDK flushes automatically at the configured interval (flushIntervalMs, default 5 seconds) and when the queue reaches the batch size. You rarely need to call flush() yourself.
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 the stored user data: anonymous ID, user ID, session, attribution, and the stored event queue. Call this on logout.
vendo.reset();After calling reset():
- A new anonymous ID is generated on the next page load. Until then, events on the current page keep the old anonymous ID
- User ID is cleared
- Session is reset
- Attribution data (UTMs, click IDs) is cleared
- The stored event queue is cleared. Events that are already queued in memory on the current page are still sent
Privacy
optOut()
Opt the user out of tracking. The SDK does not collect or send events.
vendo.optOut();optOut() immediately purges any queued events (including the persisted localStorage queue). It also writes a persistent opt-out flag, so the opt-out survives page reloads. On the next page load, the SDK starts opted out. It does not replay events or resume tracking.
Known issue: If you call
identify()while the user is opted out, the SDK does not send an event. But it saves the user ID in memory and in localStorage. AfteroptIn(), later events include that user ID.
optIn()
Opt the user back in after an optOut() call. It clears the persisted opt-out flag and enables tracking again.
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 SDK merges the values into the current consent state. It attaches them to every later event at context.consent.
vendo.setConsent({ analytics: true, marketing: false });If you initialized the SDK with waitForConsent: true, the SDK holds events until a required consent group is granted (consent.requiredGroups, default ["analytics"]). When setConsent grants 1 of these groups, the SDK stops holding. It then flushes the events that it held while it waited. A decision that grants none of the required groups (for example, “Reject all”, or necessary: true alone) resolves as denied. The SDK then purges the held events. Without waitForConsent: true, the SDK adds consent values to events, but consent never gates sending.
With waitForConsent: true, after the SDK resolves as denied, it drops every later event. It does not hold them. The SDK sends events again only after consent grants a required group.
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. The anonymous ID, user ID, session, and attribution stay in localStorage. Use reset() when you want to clear stored data (for example, 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 |
Known issue: The SDK does not send the value of the Meta
_fbccookie. It sends only thefbclidURL parameter.
First-Touch vs Last-Touch
The SDK stores both first-touch and last-touch values:
- First-touch (for example,
utm_source_firstandgclid_first): Set once, on the first visit that has tracking parameters. Never overwritten. - Last-touch (for example,
utm_source_lastandgclid_last): Updated on every visit with new URL parameters.
The SDK attaches only the last-touch values to events: in context.campaign, in context.clickId, and as flat properties such as utm_source.
Session Management
The SDK tracks sessions automatically with 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 |
vendo_optout | The opt-out flag (1). optOut() sets it and optIn() removes it |
reset() clears vendo and vendo_queue. It does not clear vendo_optout.
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
- 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
If a request fails with a network error, a timeout, a 429 status, or a 5xx status, the SDK retries it automatically with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | ~1 second |
| 2nd retry | ~2 seconds |
| 3rd retry | ~4 seconds |
The delay doubles after each failed send, up to retryMaxMs (default: 5 minutes). If an event fails maxAttempts times (default: 20), the SDK drops it.
The SDK does not retry other failed responses. It drops those events.
Events remain in the queue across page loads (persisted in localStorage). If the queue exceeds maxQueueSize (default: 200), the SDK drops the oldest events.
Size Limits
The SDK measures the size of each event as UTF-8 JSON.
- If an event is larger than
maxEventBytes(default: 32,000 bytes), the SDK drops it. It does not queue or send the event. - The SDK puts no more than
maxBatchBytes(default: 256,000 bytes) of events in 1 request. Events that do not fit wait for the next flush. - If a queued event is larger than
maxBatchBytes, the SDK drops it at flush.
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(Array.prototype.slice.call(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. The component 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) | { page: () => void };
}
}
export function TrackPageViews() {
const location = useLocation();
useEffect(() => {
if (typeof window.vendo === 'function') {
window.vendo('page');
} else {
window.vendo?.page();
}
}, [location]);
return null;
}Add it inside your <BrowserRouter>:
<BrowserRouter>
<TrackPageViews />
<Routes>{/* your routes */}</Routes>
</BrowserRouter>Framework Guides
For framework-specific setup (for example, route-change tracking and plugins), see the dedicated guides:
Related
- Quickstart: Get started in 5 minutes
- API Reference:
/collectendpoint and event schema - Self-Hosting: Deploy the ingestion API