zelto-sdk
v0.3.0
Published
zelto-sdk – Isomorphic TypeScript/JavaScript SDK for Zelto ai-platform analytics (events, identify, auto-capture)
Readme
zelto-sdk
Isomorphic TypeScript/JavaScript SDK for the Zelto ai-platform analytics backend. Use the same zelto.init, zelto.track, and zelto.identify API in browser and Node.
Install
npm install zelto-sdk
# or
pnpm add zelto-sdk
# or
yarn add zelto-sdkQuick start
import { zelto } from 'zelto-sdk';
zelto.init({ apiKey: 'your_api_key' });
zelto.track('button_clicked', { button_id: 'signup', page: 'home' });
// Optional: identify user (e.g. after login)
zelto.identify('anonymous-uuid-from-cookie', 'user_123', { email: '[email protected]' });Implementing in a new project
1. Call init once at startup
Initialize the SDK once before any track or identify calls. Do not call init on every request or on every component render.
Browser (SPA / static site)
Call init in your app entry (e.g. main.tsx, App.tsx, or the script that runs first).
// src/main.tsx (React) or equivalent
import { zelto } from 'zelto-sdk';
zelto.init({
apiKey: import.meta.env.VITE_ZELTO_API_KEY, // or process.env.NEXT_PUBLIC_ZELTO_API_KEY, etc.
capturePageViews: true, // optional: send page_view on first load
captureClicks: true, // optional: auto-track link/button clicks
});Node (server / API)
Call init when your process starts (e.g. top-level in your server file or after config is loaded).
// server/index.ts
import { zelto } from 'zelto-sdk';
zelto.init({
apiKey: process.env.ZELTO_API_KEY!,
baseUrl: process.env.ZELTO_BASE_URL, // optional; defaults to production
userAgent: 'MyApp/1.0',
});Using environment variables
- Browser: Use your bundler’s env (e.g.
VITE_*,NEXT_PUBLIC_*) so the API key is inlined at build time. Never commit secrets; use a build-time or runtime config from your backend if you need to hide the key. - Node: Use
process.env.ZELTO_API_KEY(or similar) and set it in your environment or.envfile.
2. Track custom events with track
Use track for any custom event (e.g. form submit, feature use, signup).
import { zelto } from 'zelto-sdk';
zelto.track('signup_completed', { plan: 'pro', source: 'pricing_page' });
zelto.track('article_viewed', { article_id: '123', title: 'Getting started' });- Property types: Only
string,number, andbooleanare supported. Values are stringified before send. - Context: In the browser,
page_url,page_path,referrer, anduser_agentare added automatically. In Node, onlyuser_agentis added if you pass it ininit.
3. Identify users (optional)
Call identify when you know who the user is (e.g. after login). Pass the same distinctId you used for anonymous events (e.g. from a cookie or from the SDK’s stored ID) and your internal userId.
Browser: The SDK stores a stable distinct_id in localStorage under zelto_track_distinct_id. To link anonymous activity to a known user:
- After login, read the current distinct ID (e.g. from your own cookie that you set from the same value, or by calling
initand then using your stored anonymous ID). - Call
identify(anonymousDistinctId, loggedInUserId, { email, name, ... }).
// After successful login
import { zelto } from 'zelto-sdk';
const anonymousId = getStoredAnonymousId(); // e.g. from cookie or your own storage
zelto.identify(anonymousId, user.id, { email: user.email, name: user.name });Node: There is no persistent distinct ID; each process gets an in-memory UUID. For server-side events, pass a stable identifier (e.g. session ID or user ID) as distinctId and optionally userId.
4. Flush before exit (recommended)
Events are batched and sent on a timer or when the queue reaches batchSize. To avoid losing events on page close or process exit, call flush() and await it.
Browser (page unload):
import { zelto } from 'zelto-sdk';
window.addEventListener('beforeunload', () => {
zelto.flush(); // fire-and-forget; best-effort
});
// Or use sendBeacon in the future if the SDK supports itNode (graceful shutdown):
import { zelto } from 'zelto-sdk';
process.on('beforeExit', async () => {
await zelto.flush();
});5. SPA / client-side routing
The SDK does not listen to history or hash changes. For single-page apps, send a page_view on each route change yourself:
// Example: React Router
import { zelto } from 'zelto-sdk';
// In your router or layout
useEffect(() => {
zelto.track('page_view', {
page_path: location.pathname,
page_url: location.href,
referrer: document.referrer || undefined,
});
}, [location.pathname]);You can still use capturePageViews: true for the initial load; then use zelto.track('page_view', ...) only for subsequent route changes.
6. Framework-specific tips
- Next.js (App Router): Call
initin a root layout or a client component that mounts once. UseNEXT_PUBLIC_ZELTO_API_KEYfor client-side init. For server components or API routes, callinitat server startup and usetrackin server code with appropriate distinct/user IDs. - Vite / CRA: Call
initinmain.tsx; useimport.meta.env.VITE_ZELTO_API_KEYorprocess.env.REACT_APP_ZELTO_API_KEY. - Node/Express: Call
initonce when the app starts. Usetrackin route handlers or middleware; pass a request-scoped distinct ID (e.g. from session or cookie) if you need to attribute server events to a user.
API reference
init(options)
Initialize the SDK once before using track or identify. Throws if apiKey is missing.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| apiKey | string | required | API key; org is derived server-side. |
| baseUrl | string | https://app.zelto.ai/api/v1 | Base URL for events and identify. |
| capturePageViews | boolean | false | (Browser) Send a page_view event on initial load. |
| captureClicks | boolean \| { selector?: string } | false | (Browser) Capture clicks on links/buttons via delegation. |
| batchSize | number | 10 | Flush when queue reaches this size (max 1000). |
| flushIntervalMs | number | 5000 | Flush at most every N ms. |
| userAgent | string | — | (Node) Optional user-agent string for events. |
- SPA page views: For single-page apps, call
track('page_view', { page_path: '...' })in your router on route change; the SDK does not listen to history by default. - Custom click selector: Use
captureClicks: { selector: '.my-cta, [data-track]' }to limit which elements are tracked. Default selector:a, button, [role="button"], input[type="submit"], input[type="button"].
track(eventName, properties?)
Enqueue a custom event. Events are batched and sent to POST {baseUrl}/analytics/events.
- eventName: string (e.g.
'button_clicked','signup_completed'). - properties: optional object. Only
string,number, andbooleanvalues are supported; they are stringified before send.
Context added automatically when available: page_url, page_path, referrer, user_agent, plus distinct_id and optional user_id from the current identity.
flush(): Promise<void>
Send all pending events in the queue. Resolves when the queue is empty. Use before page unload or process exit for graceful shutdown (e.g. window.addEventListener('beforeunload', () => flush()) or process.on('beforeExit', async () => { await flush(); })).
identify(distinctId, userId?, properties?)
Send user identity to POST {baseUrl}/analytics/users/identify and set in-memory identity for subsequent events. Fire-and-forget; does not block.
- distinctId: stable anonymous device/session ID (e.g. from cookie or SDK’s stored ID).
- userId: optional logged-in user ID from your system.
- properties: optional traits (e.g.
email,name). Can be any JSON-serializable values for identify; event properties remain string/number/boolean.
Browser: A stable distinct_id is generated and stored in localStorage under zelto_track_distinct_id (with in-memory fallback when storage is unavailable). Subsequent events use this ID until you call identify with a different one.
Node: A UUID is generated per process and kept in memory only.
Auto-capture (browser)
When capturePageViews: true, the SDK sends a single page_view event on initial load with page_url, page_path, and referrer.
When captureClicks: true (or captureClicks: { selector: '...' }), the SDK uses event delegation to send element_clicked events for matching elements. Each event includes:
element– tag name (e.g.a,button)id– elementidif settext– trimmed text content (truncated to 200 chars)href– for<a>, thehrefvalue
Use a custom selector to restrict which elements are tracked.
Reliability and retries
- Events are sent in batches. On network or server errors, the SDK retries 429 and 5xx with exponential backoff (up to 3 attempts by default). 4xx (other than 429) are not retried.
- Failed batches are re-queued and sent on the next flush (by timer or
flush()). Callflush()on page unload or process exit to reduce the chance of losing events.
Builds
- ESM:
import { zelto } from 'zelto-sdk'thenzelto.init(),zelto.track(), etc. - CJS:
const { zelto } = require('zelto-sdk')thenzelto.init(),zelto.track(), etc. - TypeScript declarations are included.
License
MIT
