Browser SDK
The Zeotap browser SDK is a lightweight JavaScript library (~2.7 KB gzipped) that captures events from your website and sends them to the Zeotap ingest API. Its API mirrors analytics.js so you can drop it in alongside or replace an existing tracker with minimal changes.
Install
Script Tag
Add this snippet to your website’s <head> to start sending events:
<script>
!function(){var s=window.zeotap=window.zeotap||[];
if(!s.initialized){s.methods=["load","track","identify",
"page","screen","group","alias","setConsent","reset"];
s.factory=function(m){return function(){
var a=Array.prototype.slice.call(arguments);a.unshift(m);
s.push(a);return s}};for(var i=0;i<s.methods.length;i++){
var m=s.methods[i];s[m]=s.factory(m)}
var e=document.createElement("script");e.type="text/javascript";
e.async=!0;e.src="https://content.zeotap.com/composable-sdk/v1/zeotap.min.js";
var n=document.getElementsByTagName("script")[0];
n.parentNode.insertBefore(e,n);
s.load("YOUR_WRITE_KEY",{apiHost:"https://events.zeotap.com"});
}();
</script>
Replace YOUR_WRITE_KEY with the key from Streams > Event Sources in your workspace. The snippet registers a method queue so any calls made before the main bundle finishes loading are replayed once it is ready. A page view is automatically recorded on initialization (autoPage defaults to true).
You can also copy a pre-filled snippet from Streams > Event Sources in the UI.
API
load(writeKey, options?)
Initialize the SDK. Must be called before any tracking methods. Subsequent calls are no-ops.
| Option | Type | Default | Description |
|---|---|---|---|
apiHost | string | — | Ingest API host (required). Accepts an absolute URL or a relative path such as "/cdp" when proxying through your own domain — see First-Party Domain Setup |
flushAt | number | 10 | Flush the queue after this many events accumulate |
flushInterval | number | 5000 | Flush the queue every N milliseconds |
autoPage | boolean | true | Automatically call page() after load() |
debug | boolean | false | Enable verbose console logging |
firstPartyCookies | boolean | false | Persist the anonymous ID as a server-set, HttpOnly first-party cookie — survives Safari ITP’s 7-day cap. Requires apiHost to point at a proxy on your own domain (setup guide) |
credentials | "same-origin" | "include" | "same-origin" | Cookie mode for event requests. Set "include" only when the first-party proxy runs on a separate subdomain |
storage | "auto" | "cookie" | "localStorage" | "auto" | Where identifiers persist. "auto": first-party cookie on the registrable domain with a localStorage mirror, degrading to localStorage-only when cookies are unusable |
cookieDomain | string | computed | Cookie domain override (e.g. ".example.com"). Default: the computed registrable domain |
cookieExpiryDays | number | 365 | Identifier cookie expiry in days |
cookieSecure | boolean | true on https | Cookie Secure flag |
cookieSameSite | "Lax" | "Strict" | "None" | "Lax" | Cookie SameSite attribute. "None" forces Secure |
nativeAppId | string | — | Snowflake Native App install id; only needed for write keys created inside a Native App install |
consent | object | — | Consent behavior (opt-in gating, defaults, buffering) — see Consent management |
track(event, properties?)
Record a named user action.
zeotap.track("Order Completed", {
orderId: "order-456",
revenue: 99.99,
currency: "USD"
});identify(userId?, traits?)
Associate the current user with traits. The userId is persisted alongside the anonymous ID (see Identity).
zeotap.identify("user-123", {
email: "user@example.com",
plan: "pro"
});page(category?, name?, properties?)
Record a page view. Called automatically on load() unless autoPage: false.
zeotap.page("Docs", "Getting Started");screen(category?, name?, properties?)
Record a screen view (mobile-hybrid apps).
zeotap.screen("Onboarding", "Welcome");group(groupId, traits?)
Associate the current user with a group (organization, team, workspace).
zeotap.group("org-789", {
name: "Acme Inc",
plan: "enterprise"
});alias(newId, previousId?)
Alias a new user ID to a previous anonymous or identified ID. Useful at sign-up time.
zeotap.alias("user-123");setConsent(consent)
Set the user’s consent state. All subsequent events carry context.consent with the current state. Pass exactly one of the three forms below — they are mutually exclusive.
TCF v2 consent string (EU/GDPR) — the server automatically derives consent categories from the TCF purpose consent bits:
zeotap.setConsent({ tcfString: "CPXxRfAPXxRfAAfKAB..." });US Privacy string (CCPA) — the server derives advertising/marketing opt-out status:
zeotap.setConsent({ usPrivacy: "1YNN" });Pre-parsed categories — use when your CMP already provides resolved boolean values:
zeotap.setConsent({
analytics: true,
marketing: false,
advertising: false,
functional: true,
});Each call replaces the previous consent state entirely. Forwarding rules use these values to gate delivery based on required consent categories, and every event lands in the warehouse with structured consent columns (consent_string, us_privacy, consent_categories).
reset()
Clear the current user identity (user ID, anonymous ID, queue, consent state). Call this on logout so subsequent events are attributed to a new anonymous session.
zeotap.reset();flush()
Immediately send any queued events. The SDK flushes automatically when flushAt events accumulate or flushInterval elapses, so you usually don’t need to call this — it’s useful before intentionally terminating a session (e.g., right before window.location changes).
How it Works
Identity
- Anonymous ID — generated on first
load()(UUID v4) and persisted in a first-party cookie on the registrable domain (shared across subdomains) with alocalStoragemirror, degrading tolocalStorage-only where cookies are unusable (configurable viastorage). Survives across sessions and is only regenerated onreset(). - User ID — set via
identify()and persisted the same way. Every subsequent event carries both IDs when available.
Storage written by JavaScript is subject to browser protections — Safari ITP deletes it after 7 days without a visit. To keep the anonymous ID stable long-term on all browsers, enable firstPartyCookies with a proxy on your own domain: see First-Party Domain Setup.
Queue and flush
Events are buffered in an in-memory FIFO queue. The queue is bounded (500 events by default) — when full, the oldest event is dropped to prevent unbounded memory growth on clients that go offline for long periods. The queue is flushed when one of:
flushAtevents accumulate (default 10)flushIntervalmilliseconds pass (default 5 seconds)- The page is hidden (
pagehide/visibilitychange) — flushed vianavigator.sendBeaconso events are delivered even as the user navigates away flush()is called explicitly
Page visibility handling
The SDK uses pagehide and visibilitychange === "hidden" (not beforeunload) so page navigation is reliably captured on mobile Safari and does not block the browser’s Back-Forward Cache. This is the modern, BFCache-friendly approach.
Transport and retry
Each flush sends a POST to the configured apiHost at /v1/batch with an X-Write-Key header. The sendBeacon fallback for page-hide flushes uses a ?writeKey= query parameter instead, because sendBeacon cannot set custom headers. Failed requests are retried up to 3 times with exponential backoff. After exhaustion, the events are dropped and a debug log is emitted if debug: true.
Payload Format
Events follow the Segment spec:
{
"type": "track",
"messageId": "uuid-v4",
"userId": "user-123",
"anonymousId": "uuid-v4",
"event": "Order Completed",
"properties": { "orderId": "456", "revenue": 99.99 },
"context": {
"library": { "name": "@zeotap/browser-sdk", "version": "0.1.0" },
"page": {
"url": "https://example.com/checkout",
"title": "Checkout",
"referrer": "https://example.com/cart",
"path": "/checkout",
"search": ""
},
"userAgent": "Mozilla/5.0 ...",
"locale": "en-US",
"consent": { "tcfString": "CPXx..." }
},
"timestamp": "2026-04-10T12:34:56.789Z",
"sentAt": "2026-04-10T12:34:56.789Z"
}
See Sending events for the full Segment-compatible schema.
Next Steps
- SDK Playground — Try the SDK interactively in your browser
- Event sources — Create and manage write keys
- Event forwarding — Route SDK events to destinations and warehouses
- Consent management — Configure consent categories
- Debugging — Verify events from the SDK are arriving