Tracking SDK
The OmniSocial Tracking SDK is a lightweight client-side JavaScript library that captures user events and sends them to the server-side tracking pipeline. It is built as a standalone package at packages/tracking-sdk/.
Architecture
The SDK is composed of five modules, each with a single responsibility:
Modules
Core (core.ts)
The core module provides the omni() function and manages the command queue.
Responsibilities:
- Expose the global
omni()function that accepts commands - Process queued commands from the IIFE snippet loader (
omni.q) - Route commands to the appropriate handler:
init,track,identify,consent - Maintain SDK state: pixel ID, API key, session ID, customer identity
Key internals:
type OmniCommand = 'init' | 'track' | 'identify' | 'consent';
interface SdkState {
pixelId: string;
apiKey: string;
sessionId: string;
identity: HashedIdentity | null;
consentGranted: boolean;
initialized: boolean;
}
On init, the core module:
- Stores the pixel ID and API key
- Generates or retrieves a session ID from cookies
- Captures UTM parameters and click IDs from the URL
- Flushes any pre-init queued commands
Hash (hash.ts)
Handles SHA-256 hashing of personally identifiable information before it leaves the browser.
Hashed fields:
| Field | Input Normalization | Hashing |
|---|---|---|
email | Lowercase, trim whitespace | SHA-256 hex |
phone | Remove spaces, dashes, parentheses; ensure + prefix | SHA-256 hex |
firstName | Lowercase, trim | SHA-256 hex |
lastName | Lowercase, trim | SHA-256 hex |
Implementation:
async function hashValue(value: string): Promise<string> {
const encoder = new TextEncoder();
const data = encoder.encode(value.trim().toLowerCase());
const buffer = await crypto.subtle.digest('SHA-256', data);
return Array.from(new Uint8Array(buffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
Uses the Web Crypto API (crypto.subtle) which is available in all modern browsers. No external dependencies required.
Cookies (cookies.ts)
Manages first-party cookies for identity persistence and click ID storage.
Cookies managed:
| Cookie | Content | Max Age | Purpose |
|---|---|---|---|
_omni_id | Hashed visitor ID | 365 days | Cross-session identity |
_omni_sess | Session ID | 30 minutes (sliding) | Session tracking |
_omni_fbclid | Facebook click ID | 7 days | Meta CAPI attribution |
_omni_gclid | Google click ID | 90 days | Google Ads attribution |
_omni_ttclid | TikTok click ID | 7 days | TikTok attribution |
_omni_utm | JSON-encoded UTM params | Session | Campaign attribution |
Cookie attributes:
SameSite=Lax-- Allows cookies on top-level navigations but blocks cross-site subrequestsSecure-- Set only on HTTPS originsPath=/-- Available across all pages on the domain
Transport (transport.ts)
Handles event delivery to the server with automatic fallback.
Delivery strategy:
- Primary: Beacon API -- Uses
navigator.sendBeacon()for fire-and-forget delivery. Reliable even during page unload events. - Fallback: XHR -- Falls back to
XMLHttpRequestif Beacon is unavailable or if the payload exceeds the Beacon size limit (64 KB). - Retry -- Failed XHR requests are retried up to 2 times with exponential backoff (1s, 3s).
Request format:
The shape below is out of date. The SDK actually sends POST /api/v1/tracking/ingest (or /batch) with a snake-case body: pixel_id, api_key, event_name, event_id, event_time, action_source, source_url, user_data (hashed), custom_data. Unload flushes use sendBeacon with text/plain. The default endpoint is https://api.unipulse.tech/api/v1/tracking/ingest. event_id defaults to purchase:<order_id> for a Purchase, otherwise a UUID, and track() returns it.
POST /tracking/events
Content-Type: application/json
x-api-key: {apiKey}
{
"pixelId": "px_abc123",
"eventName": "Purchase",
"eventId": "evt_unique_id",
"eventData": { "value": 49.99, "currency": "USD" },
"sourceUrl": "https://example.com/checkout/success",
"sessionId": "sess_xyz",
"identity": { "em": "a1b2c3...", "ph": "d4e5f6..." },
"utm": { "source": "facebook", "medium": "cpc", "campaign": "summer" },
"clickIds": { "fbclid": "abc123" },
"userAgent": "Mozilla/5.0...",
"timestamp": 1741392000000
}
Consent (consent.ts)
Implements GDPR-compliant consent management.
Behavior:
| State | Tracking | Event Queue |
|---|---|---|
| Consent not set | Blocked | Events are queued in memory |
omni('consent', true) | Active | Queued events are flushed, new events fire immediately |
omni('consent', false) | Blocked | Queue is cleared, no events are sent |
Integration with CMP:
The SDK does not include a consent banner. It is designed to work with any Consent Management Platform (CMP). Call omni('consent', true) from your CMP's callback when the user grants tracking consent.
// Example: integration with a generic CMP
myCMP.onConsentGranted(() => {
omni('consent', true);
});
Build Configuration
The SDK is built using tsup (esbuild-based bundler) with three output formats:
| Format | Entry | Output | Use Case |
|---|---|---|---|
| IIFE | src/iife.ts | dist/omni.min.js | Script tag installation |
| ESM | src/index.ts | dist/index.mjs | Modern bundlers (Vite, webpack 5) |
| CJS | src/index.ts | dist/index.cjs | Node.js / SSR environments |
Size Budget
| Target | Limit | Measured |
|---|---|---|
| IIFE (gzipped) | < 4 KB | ~3.2 KB |
| ESM (gzipped) | < 3 KB | ~2.8 KB |
The SDK must remain dependency-free to stay under the size budget. Do not add external packages. All functionality (hashing, cookies, transport) is implemented using browser-native APIs.
API Reference
omni('init', pixelId, options)
Initialize the SDK. Must be called before any other command.
| Parameter | Type | Required | Description |
|---|---|---|---|
pixelId | string | Yes | The tracking pixel ID |
options.apiKey | string | Yes | The pixel API key |
options.autoPageView | boolean | No | Automatically track PageView on init (default: true) |
options.cookieDomain | string | No | Override the cookie domain (default: current domain) |
omni('track', eventName, eventData?)
Track a custom event.
| Parameter | Type | Required | Description |
|---|---|---|---|
eventName | string | Yes | Event name (e.g., Purchase, AddToCart) |
eventData | object | No | Arbitrary key-value pairs |
Common events:
| Event | Required Data | Optional Data |
|---|---|---|
PageView | -- | title, referrer |
ViewContent | content_id | content_name, content_type |
AddToCart | product_id, value, currency | quantity, product_name |
InitiateCheckout | value, currency | item_count |
Purchase | order_id, value, currency | items[], coupon |
Lead | -- | lead_type, value |
omni('identify', traits)
Identify the current user. Traits are hashed before transmission.
| Parameter | Type | Required | Description |
|---|---|---|---|
traits.email | string | No | User email (hashed to SHA-256) |
traits.phone | string | No | User phone (hashed to SHA-256) |
traits.externalId | string | No | Your internal user ID |
omni('consent', granted)
Set tracking consent.
| Parameter | Type | Required | Description |
|---|---|---|---|
granted | boolean | Yes | true to enable tracking, false to disable |
Testing Locally
To build and test the SDK during development:
cd packages/tracking-sdk
npm run build # Build all formats
npm run dev # Watch mode with hot reload
npm run size # Check bundle size against budget
To test against the local API:
omni('init', {
pixelId: 'tp_xxxxxxxxxxxxxxxx',
apiKey: 'test_key',
endpoint: 'http://localhost:3001/api/v1/tracking/ingest', // Override for local dev
});
- Tracking & CAPI System -- Server-side pipeline architecture
- Services -- Full service layer overview
- Shared Package -- Shared TypeScript types
- New Feature Checklist -- End-to-end feature guide