@zixflow/analytics-node
v1.1.5
Published
Complete guide for integrating the Zixflow JavaScript SDK into your browser and Node.js applications.
Downloads
916
Readme
Zixflow JavaScript SDK - Integration Guide
Complete guide for integrating the Zixflow JavaScript SDK into your browser and Node.js applications.
Table of Contents
- Introduction
- Requirements
- Installation
- Quick Start
- Core Features
- Browser-Specific Features
- Node.js-Specific Features
- Web Push Notifications
- Push Notification Tracking
- Advanced Configuration
- TypeScript Support
- Example Apps
- API Reference
Introduction
The Zixflow JavaScript SDK enables you to send data from your browser and Node.js applications to Zixflow, allowing you to track user behavior, send targeted push notifications, display in-app messages, and more.
SDK Packages
The SDK is distributed as three npm packages:
- @zixflow/analytics-browser: For web browsers (client-side)
- @zixflow/analytics-node: For Node.js servers (server-side)
- @zixflow/analytics-core: Shared core functionality (automatically installed)
SDK Features
Browser SDK:
- User identification and event tracking
- Web push notifications with service workers
- In-app messaging and inbox
- Page view tracking
- Automatic page enrichment
- Video plugin support (YouTube, Vimeo)
Node.js SDK:
- User identification and event tracking
- Server-side analytics
- Event batching and retries
- Flexible configuration
- TypeScript support
Requirements
Browser
- Modern browsers: Chrome, Firefox, Safari, Edge (latest versions)
- ES6 support: Required for optimal performance
- Service Worker support: Required for web push notifications
- HTTPS: Required for web push notifications
Node.js
- Node.js: 14.0 or higher
- npm: 6.0 or higher
- TypeScript: 4.0+ (optional, for TypeScript projects)
Installation
The Zixflow JavaScript SDK is available on npm. The latest version is 1.1.5.
📦 Browser SDK: npm - @zixflow/analytics-browser 📦 Node.js SDK: npm - @zixflow/analytics-node
Browser SDK
Install via npm:
npm install @zixflow/analytics-browser@^1.1.5Or via yarn:
yarn add @zixflow/analytics-browser@^1.1.5Node.js SDK
Install via npm:
npm install @zixflow/analytics-node@^1.1.5Or via yarn:
yarn add @zixflow/analytics-nodeQuick Start
Browser Quick Start
import { AnalyticsBrowser } from '@zixflow/analytics-browser'
// Initialize with your API key
const analytics = AnalyticsBrowser.load({
apiKey: 'YOUR_API_KEY'
})
// Identify a user
analytics.identify('[email protected]', {
first_name: 'John',
last_name: 'Doe',
email: '[email protected]'
})
// Track an event
analytics.track('button_clicked', {
button_name: 'signup',
page: 'homepage'
})
// Track a page view
analytics.page('Home', {
title: 'Homepage',
url: window.location.href
})Node.js Quick Start
import { Analytics } from '@zixflow/analytics-node'
// Initialize with your API key
const analytics = new Analytics({
writeKey: 'YOUR_API_KEY'
})
// Identify a user
analytics.identify({
userId: '[email protected]',
traits: {
first_name: 'John',
last_name: 'Doe',
email: '[email protected]'
}
})
// Track an event
analytics.track({
userId: '[email protected]',
event: 'purchase_completed',
properties: {
product_id: '123',
price: 29.99,
currency: 'USD'
}
})
// Flush events before exit
await analytics.closeAndFlush()Core Features
User Identification
Identify users to track their activity across sessions.
Browser
// Identify with user ID only
analytics.identify('[email protected]')
// Identify with traits
analytics.identify('[email protected]', {
first_name: 'John',
last_name: 'Doe',
email: '[email protected]',
plan: 'premium',
created_at: new Date().toISOString()
})
// With callback
analytics.identify('[email protected]', {
email: '[email protected]'
}, () => {
console.log('User identified successfully')
})Node.js
// Identify with user ID and traits
analytics.identify({
userId: '[email protected]',
traits: {
first_name: 'John',
last_name: 'Doe',
email: '[email protected]',
plan: 'premium'
}
})
// With anonymous ID
analytics.identify({
userId: '[email protected]',
anonymousId: 'anon-123',
traits: {
email: '[email protected]'
}
})
// With callback
analytics.identify({
userId: '[email protected]',
traits: { email: '[email protected]' }
}, (err) => {
if (err) {
console.error('Identification failed:', err)
} else {
console.log('User identified successfully')
}
})Event Tracking
Track custom events to understand user behavior.
Browser
// Simple event
analytics.track('button_clicked')
// Event with properties
analytics.track('purchase_completed', {
product_id: '123',
product_name: 'Widget',
price: 29.99,
currency: 'USD',
quantity: 1
})
// With callback
analytics.track('signup_completed', {
method: 'email'
}, () => {
console.log('Event tracked successfully')
})Node.js
// Track event with user ID
analytics.track({
userId: '[email protected]',
event: 'purchase_completed',
properties: {
product_id: '123',
product_name: 'Widget',
price: 29.99,
currency: 'USD'
}
})
// Track event with anonymous ID
analytics.track({
anonymousId: 'anon-123',
event: 'page_viewed',
properties: {
page: 'homepage'
}
})
// With timestamp
analytics.track({
userId: '[email protected]',
event: 'order_completed',
properties: { order_id: 'ABC123' },
timestamp: new Date()
})
// With callback
analytics.track({
userId: '[email protected]',
event: 'signup_completed'
}, (err) => {
if (err) {
console.error('Tracking failed:', err)
}
})Page Tracking
Track page views in your web application.
Browser
// Track current page
analytics.page()
// Track with page name
analytics.page('Home')
// Track with category and name
analytics.page('Docs', 'Getting Started')
// Track with properties
analytics.page('Product Detail', {
product_id: '123',
category: 'Electronics',
url: window.location.href,
referrer: document.referrer
})
// Automatic page tracking on route changes
// For Single Page Applications (SPAs):
window.addEventListener('popstate', () => {
analytics.page()
})Node.js
// Track page view
analytics.page({
userId: '[email protected]',
name: 'Home',
properties: {
title: 'Homepage',
url: 'https://example.com'
}
})
// With category
analytics.page({
userId: '[email protected]',
category: 'Docs',
name: 'Getting Started',
properties: {
url: 'https://example.com/docs/getting-started'
}
})Screen Tracking
Track screen views in your application.
Browser
// Track screen view
analytics.screen('Dashboard', {
user_type: 'premium'
})Node.js
// Track screen view
analytics.screen({
userId: '[email protected]',
name: 'Dashboard',
properties: {
user_type: 'premium',
version: '2.0'
}
})Group Tracking
Associate users with groups or organizations.
Browser
// Associate user with group
analytics.group('company-123', {
name: 'Acme Inc',
plan: 'enterprise',
employees: 100
})Node.js
// Associate user with group
analytics.group({
userId: '[email protected]',
groupId: 'company-123',
traits: {
name: 'Acme Inc',
plan: 'enterprise',
employees: 100,
industry: 'technology'
}
})Alias
Merge two user identities.
Browser
// Merge anonymous user with identified user
analytics.alias('[email protected]', 'anon-123')Node.js
// Merge user identities
analytics.alias({
userId: '[email protected]',
previousId: 'anon-123'
})Reset/Logout
Clear user identification when they log out.
Browser
// Clear user identification
analytics.reset()
// Good practice: reset on logout
function handleLogout() {
analytics.reset()
// ... rest of logout logic
}Browser-Specific Features
Automatic Page Enrichment
The browser SDK automatically enriches page events with contextual information:
- Page URL and referrer
- Page title
- User agent
- Screen dimensions
- Locale and timezone
- Campaign parameters (UTM tags)
Loading State
// Wait for analytics to be ready
analytics.ready(() => {
console.log('Analytics is ready!')
// Perform actions that depend on analytics
})Plugin System
Extend the SDK with custom plugins:
const customPlugin = {
name: 'Custom Plugin',
type: 'enrichment',
version: '1.0.0',
isLoaded: () => true,
load: async (ctx, analytics) => {
console.log('Plugin loaded')
},
track: async (ctx) => {
// Modify or enrich track events
ctx.event.properties = {
...ctx.event.properties,
custom_property: 'value'
}
return ctx
}
}
analytics.register(customPlugin)CDN Usage
Load the SDK directly from CDN:
<!DOCTYPE html>
<html>
<head>
<script>
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Zixflow snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on","addSourceMiddleware","addIntegrationMiddleware","setAnonymousId","addDestinationMiddleware"];analytics.factory=function(e){return function(){var t=Array.prototype.slice.call(arguments);t.unshift(e);analytics.push(t);return analytics}};for(var e=0;e<analytics.methods.length;e++){var key=analytics.methods[e];analytics[key]=analytics.factory(key)}analytics.load=function(key,e){var t=document.createElement("script");t.type="text/javascript";t.async=!0;t.src="https://cdn.zixflow.com/analytics.js/v1/"+key+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n);analytics._loadOptions=e};analytics._writeKey="YOUR_API_KEY";analytics.SNIPPET_VERSION="4.15.3";
analytics.load("YOUR_API_KEY");
analytics.page();
}}();
</script>
</head>
<body>
<h1>My Website</h1>
</body>
</html>Node.js-Specific Features
Event Batching
The Node.js SDK automatically batches events for efficiency:
const analytics = new Analytics({
writeKey: 'YOUR_API_KEY',
maxEventsInBatch: 15, // Default: 15 events per batch
flushInterval: 10000 // Default: 10 seconds
})Flushing Events
Ensure all events are sent before application exit:
// Flush all pending events
await analytics.closeAndFlush()
// With timeout
await analytics.closeAndFlush({ timeout: 5000 })
// Good practice: flush on process exit
process.on('SIGTERM', async () => {
await analytics.closeAndFlush()
process.exit(0)
})Error Handling
Handle errors with event emitters:
analytics.on('error', (err) => {
console.error('Analytics error:', err)
})
analytics.on('http_request', (event) => {
console.log('HTTP request:', event)
})Callbacks
Use callbacks for individual events:
analytics.track({
userId: '[email protected]',
event: 'signup_completed'
}, (err, batch) => {
if (err) {
console.error('Failed to track event:', err)
} else {
console.log('Event tracked successfully')
}
})Web Push Notifications
Enable web push notifications in the browser to send timely, engaging messages to users.
** Track your campaigns:** The SDK automatically tracks delivery, opens, and clicks. For advanced tracking customization and implementation details, see Push Notification Tracking.
Prerequisites
- HTTPS: Web push requires a secure context (HTTPS)
- Service Worker: A service worker file to handle push events
- API Key: Your Zixflow API key
- User Permission: Users must grant notification permission
Step 1: Install Dependencies
npm install @zixflow/analytics-browserStep 2: Create Service Worker
Create a file public/sw.js (or public/zixflow-sw.js):
// Service Worker for Zixflow Web Push Notifications
self.addEventListener('install', (event) => {
console.log('[SW] Installing service worker')
self.skipWaiting()
})
self.addEventListener('activate', (event) => {
console.log('[SW] Activating service worker')
event.waitUntil(self.clients.claim())
})
self.addEventListener('push', (event) => {
console.log('[SW] Push event received')
if (!event.data) {
console.warn('[SW] Push event has no data')
return
}
let data
try {
data = event.data.json()
} catch (e) {
console.error('[SW] Failed to parse push data:', e)
return
}
const title = data.title || 'Notification'
const options = {
body: data.body || '',
icon: data.icon || '/icon.png',
badge: data.badge || '/badge.png',
image: data.image,
data: data,
tag: data.tag || 'zixflow-push',
requireInteraction: data.requireInteraction || false,
actions: data.actions || []
}
event.waitUntil(
self.registration.showNotification(title, options)
)
})
self.addEventListener('notificationclick', (event) => {
console.log('[SW] Notification clicked:', event.action)
event.notification.close()
const clickedUrl = event.action
? event.notification.data.actions?.[event.action]?.url
: event.notification.data.url || '/'
event.waitUntil(
clients.openWindow(clickedUrl).then((windowClient) => {
if (windowClient) {
windowClient.focus()
}
})
)
})Step 3: Initialize Web Push Plugin
import { AnalyticsBrowser } from '@zixflow/analytics-browser'
// Initialize analytics with web push
const analytics = AnalyticsBrowser.load({
apiKey: 'YOUR_API_KEY',
plugins: [
{
name: 'WebPush',
settings: {
apiHost: 'api-events.zixflow.com/v1', // Optional
protocol: 'https', // Optional
swUrl: '/sw.js', // Path to service worker
autoSubscribe: false, // Don't auto-request permission
onNotificationClick: (url, action, data) => {
// Handle notification clicks when app is open
console.log('Notification clicked:', url, action, data)
// Navigate to URL or perform action
window.location.href = url
}
}
}
]
})Step 4: Request Push Permission
Request permission on user action (e.g., button click):
// HTML
<button id="subscribe-btn">Enable Notifications</button>
// JavaScript
document.getElementById('subscribe-btn').addEventListener('click', async () => {
try {
await analytics.plugins.WebPush.subscribeToPush()
console.log('Push notifications enabled')
} catch (err) {
console.error('Failed to enable push notifications:', err)
}
})Step 5: Identify Users
Associate push subscription with user:
// After user logs in
analytics.identify('[email protected]', {
email: '[email protected]',
first_name: 'John'
})
// The push subscription is automatically linked to the userWeb Push API Reference
Subscribe to Push
// Request permission and subscribe
await analytics.plugins.WebPush.subscribeToPush()Unsubscribe from Push
// Unsubscribe from push notifications
await analytics.plugins.WebPush.unsubscribeFromPush()Check Subscription Status
// Check if user is subscribed
const subscription = await analytics.plugins.WebPush.getSubscription()
console.log('Subscription:', subscription)Web Push Best Practices
- Request permission thoughtfully - Only ask after explaining value
- Use user gestures - Request permission on button clicks, not page load
- Provide unsubscribe option - Let users easily disable notifications
- Test on HTTPS - Web push requires secure context
- Handle errors gracefully - Permission can be denied or blocked
- Use meaningful content - Provide value in notifications
- Deep link properly - Navigate users to relevant content
Web Push Troubleshooting
Permission Denied
Problem: User denied notification permission
Solution:
- Explain value before requesting permission
- Guide users to browser settings to re-enable
- Provide clear unsubscribe option
Service Worker Not Registered
Error: Failed to register service worker
Solution:
// Check service worker registration
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('SW registered:', reg))
.catch(err => console.error('SW registration failed:', err))
}Push Not Received
Checklist:
- [ ] HTTPS enabled (required for web push)
- [ ] Service worker registered successfully
- [ ] User granted notification permission
- [ ] User identified with
analytics.identify() - [ ] Push subscription active
- [ ] Browser supports web push
Push Notification Tracking
Overview
Zixflow automatically tracks web push notification delivery and engagement metrics to help you measure campaign performance. The SDK tracks three key lifecycle events:
- Delivery Confirmed - When the notification arrives on the device
- Notification Opened - When the user taps the notification
- Action Button Clicked - When the user taps an action button
How Push Tracking Works
When Zixflow sends a push notification, it includes special tracking fields in the data payload:
| Field | Purpose |
|-------|---------|
| Zixflow-Delivery-ID | Unique ID for this delivery (links to campaign record) |
| Zixflow-Delivery-Token | The push subscription endpoint |
Example push payload:
{
"Zixflow-Delivery-ID": "626533406292836846",
"Zixflow-Delivery-Token": "https://fcm.googleapis.com/...",
"title": "Flash Sale! 70% OFF",
"body": "Limited time only — grab your deal now!",
"deeplink_url": "https://yourapp.com/sale",
"image_url": "https://cdn.yourapp.com/banner.png",
"action_buttons": "[{\"name\":\"Shop Now\",\"deeplink\":\"https://yourapp.com/sale\"},{\"name\":\"Remind Me\",\"deeplink\":\"\"}]"
}Note: When using the Zixflow service worker implementation, tracking is handled automatically. This section covers custom implementations.
Tracking Events
1. Delivery Confirmed
When to track: The moment the push event fires in the service worker
Implementation (service-worker.js):
// service-worker.js
self.addEventListener('push', (event) => {
const data = event.data?.json() ?? {};
const deliveryId = data['Zixflow-Delivery-ID'] ?? '';
const token = data['Zixflow-Delivery-Token'] ?? '';
if (deliveryId && token) {
// Track delivery using fetch API
fetch('https://events.zixflow.in/v1/track', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${btoa(WRITE_KEY + ':')}`
},
body: JSON.stringify({
event: 'Push Notification Delivered',
userId: self.__ZIXFLOW_USER_ID__,
properties: {
'Zixflow-Delivery-ID': deliveryId,
'Zixflow-Delivery-Token': token,
},
}),
}).catch(() => {});
}
// Show notification
event.waitUntil(
self.registration.showNotification(data.title ?? 'Notification', {
body: data.body ?? '',
icon: data.image_url,
data: data,
actions: parseButtons(data.action_buttons),
})
);
});
function parseButtons(raw) {
if (!raw) return [];
try {
const decoded = typeof raw === 'string' ? JSON.parse(raw) : raw;
return decoded.slice(0, 2).map((btn, i) => ({
action: `ACTION_${i}`,
title: btn.name || `Action ${i + 1}`,
}));
} catch {
return [];
}
}2. Notification Opened
When to track: When the user clicks the notification
Implementation (service-worker.js):
// service-worker.js
self.addEventListener('notificationclick', (event) => {
const data = event.notification.data ?? {};
const deliveryId = data['Zixflow-Delivery-ID'] ?? '';
const token = data['Zixflow-Delivery-Token'] ?? '';
event.notification.close();
// Track opened
if (deliveryId && token) {
fetch('https://events.zixflow.in/v1/track', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${btoa(WRITE_KEY + ':')}`
},
body: JSON.stringify({
event: 'Push Notification Opened',
userId: self.__ZIXFLOW_USER_ID__,
properties: {
'Zixflow-Delivery-ID': deliveryId,
'Zixflow-Delivery-Token': token,
},
}),
}).catch(() => {});
}
// Handle action button click
if (event.action) {
const actionIndex = parseInt(event.action.replace(/\D/g, ''), 10);
const buttons = parseButtons(data.action_buttons);
const actionName = buttons[actionIndex]?.name ?? '';
const deeplink = buttons[actionIndex]?.deeplink ?? '';
// Track action click
fetch('https://events.zixflow.in/v1/track', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${btoa(WRITE_KEY + ':')}`
},
body: JSON.stringify({
event: 'Push Notification Action Clicked',
userId: self.__ZIXFLOW_USER_ID__,
properties: {
'Zixflow-Delivery-ID': deliveryId,
'Zixflow-Delivery-Token': token,
'action_index': actionIndex,
'action_name': actionName,
'action_deeplink': deeplink,
'source': 'web_push',
},
}),
}).catch(() => {});
if (deeplink) {
event.waitUntil(clients.openWindow(deeplink));
return;
}
}
// Default: open deeplink or focus existing tab
const deeplink = data['deeplink_url'] ?? '';
event.waitUntil(
deeplink ? clients.openWindow(deeplink) : clients.openWindow('/')
);
});3. Action Button Clicked
Action button clicks are tracked within the notificationclick event handler shown above when event.action is present.
Action button properties:
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| Zixflow-Delivery-ID | string | ✅ | Links event to campaign delivery |
| action_index | integer | ✅ | 0-based index of button tapped |
| action_name | string | ✅ | Button label (e.g., "Shop Now") |
| Zixflow-Delivery-Token | string | Recommended | Push subscription endpoint |
| action_deeplink | string | Recommended | Button's URL |
| source | string | Optional | Set to "web_push" |
Complete Service Worker Implementation
Here's a complete service worker with all tracking:
// public/zixflow-sw.js
const WRITE_KEY = 'YOUR_API_KEY';
const ZIXFLOW_API = 'https://events.zixflow.in/v1/track';
// Store user ID (set from main app)
self.__ZIXFLOW_USER_ID__ = null;
// Listen for messages from main app
self.addEventListener('message', (event) => {
if (event.data?.type === 'SET_USER_ID') {
self.__ZIXFLOW_USER_ID__ = event.data.userId;
}
});
// Track delivery
self.addEventListener('push', (event) => {
const data = event.data?.json() ?? {};
const deliveryId = data['Zixflow-Delivery-ID'] ?? '';
const token = data['Zixflow-Delivery-Token'] ?? '';
// Track delivery
if (deliveryId && token) {
trackEvent('Push Notification Delivered', {
'Zixflow-Delivery-ID': deliveryId,
'Zixflow-Delivery-Token': token,
});
}
// Parse action buttons
const buttons = parseActionButtons(data.action_buttons);
// Show notification
event.waitUntil(
self.registration.showNotification(data.title ?? 'Notification', {
body: data.body ?? '',
icon: data.image_url,
badge: data.badge_url,
data: data,
actions: buttons,
})
);
});
// Track opens and action clicks
self.addEventListener('notificationclick', (event) => {
const data = event.notification.data ?? {};
const deliveryId = data['Zixflow-Delivery-ID'] ?? '';
const token = data['Zixflow-Delivery-Token'] ?? '';
event.notification.close();
// Always track opened
if (deliveryId && token) {
trackEvent('Push Notification Opened', {
'Zixflow-Delivery-ID': deliveryId,
'Zixflow-Delivery-Token': token,
});
}
// Handle action button click
if (event.action) {
const actionIndex = parseInt(event.action.replace(/\D/g, ''), 10);
const rawButtons = parseActionButtonsRaw(data.action_buttons);
const actionName = rawButtons[actionIndex]?.name ?? '';
const actionDeeplink = rawButtons[actionIndex]?.deeplink ?? '';
trackEvent('Push Notification Action Clicked', {
'Zixflow-Delivery-ID': deliveryId,
'Zixflow-Delivery-Token': token,
'action_index': actionIndex,
'action_name': actionName,
'action_deeplink': actionDeeplink,
'source': 'web_push',
});
if (actionDeeplink) {
event.waitUntil(clients.openWindow(actionDeeplink));
return;
}
}
// Default navigation
const deeplink = data['deeplink_url'] ?? '';
event.waitUntil(
deeplink ? clients.openWindow(deeplink) : clients.openWindow('/')
);
});
// Helper: Track event
function trackEvent(eventName, properties) {
fetch(ZIXFLOW_API, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${btoa(WRITE_KEY + ':')}`,
},
body: JSON.stringify({
event: eventName,
userId: self.__ZIXFLOW_USER_ID__,
properties: properties,
}),
}).catch((err) => {
console.error('Failed to track event:', err);
});
}
// Helper: Parse action buttons for notification API
function parseActionButtons(raw) {
const buttons = parseActionButtonsRaw(raw);
return buttons.slice(0, 2).map((btn, i) => ({
action: `ACTION_${i}`,
title: btn.name || `Action ${i + 1}`,
}));
}
// Helper: Parse raw action buttons data
function parseActionButtonsRaw(raw) {
if (!raw) return [];
try {
return typeof raw === 'string' ? JSON.parse(raw) : raw;
} catch {
return [];
}
}Setting User ID in Service Worker
To associate tracking events with the correct user, pass the user ID from your main application to the service worker:
// main app (after analytics.identify())
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage({
type: 'SET_USER_ID',
userId: '[email protected]',
});
}Action Buttons Format
The action_buttons field is a JSON-encoded string:
Raw payload value:
"[{\"name\":\"Shop Now\",\"deeplink\":\"https://yourapp.com/sale\"},{\"name\":\"Remind Me\",\"deeplink\":\"\"}]"Parsed structure:
[
{ "name": "Shop Now", "deeplink": "https://yourapp.com/sale" },
{ "name": "Remind Me", "deeplink": "" }
]Rules:
- Maximum 2 buttons (browser limitation)
deeplinkmay be empty- Button index is 0-based
Tracking Decision Flowchart
Notification received (push event)?
├── YES → Track "Push Notification Delivered"
│ Show notification
│
User clicked notification?
├── Clicked body
│ └── Track "Push Notification Opened"
│ Navigate to deeplink_url
│
└── Clicked action button
├── Track "Push Notification Opened"
└── Track "Push Notification Action Clicked"
Navigate to button's deeplinkFallback for Non-Zixflow Push
If a push doesn't contain Zixflow-Delivery-ID, skip tracking or use custom event names:
// Don't use reserved push event names for non-Zixflow pushes
if (!data['Zixflow-Delivery-ID']) {
trackEvent('External Push Received', {
'title': data.title ?? '',
'body': data.body ?? '',
'source': 'external',
});
}Important: Event names
Push Notification Delivered,Push Notification Opened, andPush Notification Action Clickedare reserved for Zixflow's delivery pipeline.
Testing Push Tracking
Enable Debug Logging
Add logging to your service worker:
function trackEvent(eventName, properties) {
console.log('[Zixflow] Tracking:', eventName, properties);
fetch(ZIXFLOW_API, {
// ... existing implementation
}).then(response => {
console.log('[Zixflow] Track response:', response.status);
}).catch((err) => {
console.error('[Zixflow] Track failed:', err);
});
}View logs in browser DevTools → Application → Service Workers → Console
Check Campaign Analytics
- Log into Zixflow dashboard
- Navigate to Messaging → Push Notifications
- View campaign metrics:
- Sent - Total notifications sent
- Delivered - Arrived in browser
- Opened - User clicked notification
- Clicked - User clicked action button
Advanced Configuration
Browser Configuration
import { AnalyticsBrowser } from '@zixflow/analytics-browser'
const analytics = AnalyticsBrowser.load({
// Required
apiKey: 'YOUR_API_KEY',
// Delivery strategy
deliveryStrategy: {
strategy: 'batching', // or 'standard'
config: {
size: 10, // Batch size
timeout: 5000 // Batch timeout (ms)
}
},
// Disable page views
disablePageView: false,
// Disable client-side persistence
disableClientPersistence: false,
// Plugins
plugins: [
// Custom plugins
],
// Middleware
addSourceMiddleware: (middleware) => {
// Add source middleware
},
// Initial page view
initialPageview: true,
// Obfuscate
obfuscate: false
})Node.js Configuration
import { Analytics } from '@zixflow/analytics-node'
const analytics = new Analytics({
// Required
writeKey: 'YOUR_API_KEY',
// API path (optional)
path: '/v1/track',
// Batching
maxEventsInBatch: 15, // Max events per batch
flushInterval: 10000, // Flush interval (ms)
// Retries
maxRetries: 3, // Max retry attempts
httpRequestTimeout: 10000, // Request timeout (ms)
// Disable events
disable: false,
// Enable/disable specific features
enable: true
})Delivery Strategies (Browser)
Standard Delivery
Send each event immediately:
const analytics = AnalyticsBrowser.load({
apiKey: 'YOUR_API_KEY',
deliveryStrategy: {
strategy: 'standard'
}
})Batched Delivery
Batch events for efficiency:
const analytics = AnalyticsBrowser.load({
apiKey: 'YOUR_API_KEY',
deliveryStrategy: {
strategy: 'batching',
config: {
size: 10, // Send after 10 events
timeout: 5000 // Or after 5 seconds
}
}
})TypeScript Support
Both packages include TypeScript definitions.
Browser TypeScript Example
import { AnalyticsBrowser, Analytics } from '@zixflow/analytics-browser'
const analytics: Analytics = AnalyticsBrowser.load({
apiKey: 'YOUR_API_KEY'
})
interface UserTraits {
email: string
first_name: string
last_name: string
plan: 'free' | 'premium' | 'enterprise'
}
interface PurchaseProperties {
product_id: string
price: number
currency: string
}
// Type-safe identify
analytics.identify('[email protected]', {
email: '[email protected]',
first_name: 'John',
last_name: 'Doe',
plan: 'premium'
} as UserTraits)
// Type-safe track
analytics.track('purchase_completed', {
product_id: '123',
price: 29.99,
currency: 'USD'
} as PurchaseProperties)Node.js TypeScript Example
import { Analytics, AnalyticsSettings } from '@zixflow/analytics-node'
const settings: AnalyticsSettings = {
writeKey: 'YOUR_API_KEY',
maxEventsInBatch: 20,
flushInterval: 10000
}
const analytics = new Analytics(settings)
interface UserTraits {
email: string
first_name: string
last_name: string
}
analytics.identify({
userId: '[email protected]',
traits: {
email: '[email protected]',
first_name: 'John',
last_name: 'Doe'
} as UserTraits
})
// Flush before exit
process.on('SIGTERM', async () => {
await analytics.closeAndFlush()
process.exit(0)
})Example Apps
The SDK repository includes complete example applications demonstrating various features.
Browser Examples
Location: /examples/browser-test.html
Demonstrates:
- SDK initialization
- User identification
- Event tracking
- Page tracking
- Interactive testing UI
Running the example:
- Open
examples/browser-test.htmlin a browser - Enter your API key
- Test different tracking methods
Location: /examples/browser-test-dev.html
Pre-configured example for development:
- Uses dev API endpoint
- Includes test API key
- Ready to test immediately
Node.js Examples
Location: /examples/node-test.js
Demonstrates:
- SDK initialization
- User identification
- Event tracking
- Page and screen tracking
- Group tracking
- Proper shutdown with flush
Running the example:
cd examples
npm install
node node-test.jsTest Server
Location: /examples/test-server.js
Local HTTP server to inspect requests:
- Logs all incoming requests
- Shows request headers and body
- Useful for debugging
Running the server:
cd examples
node test-server.jsAPI Reference
Browser SDK
AnalyticsBrowser.load(settings)
Initialize the analytics instance.
Parameters:
settings(object)apiKey(string, required): Your Zixflow API keydeliveryStrategy(object, optional): Delivery strategy configurationdisablePageView(boolean, optional): Disable automatic page viewsplugins(array, optional): Custom plugins to load
Returns: Analytics instance
analytics.identify(userId, traits, options, callback)
Identify a user.
Parameters:
userId(string, required): User identifiertraits(object, optional): User attributesoptions(object, optional): Additional optionscallback(function, optional): Callback function
Returns: Promise<Context>
analytics.track(event, properties, options, callback)
Track an event.
Parameters:
event(string, required): Event nameproperties(object, optional): Event propertiesoptions(object, optional): Additional optionscallback(function, optional): Callback function
Returns: Promise<Context>
analytics.page(category, name, properties, options, callback)
Track a page view.
Parameters:
category(string, optional): Page categoryname(string, optional): Page nameproperties(object, optional): Page propertiesoptions(object, optional): Additional optionscallback(function, optional): Callback function
Returns: Promise<Context>
analytics.screen(category, name, properties, options, callback)
Track a screen view.
Parameters: Same as page()
Returns: Promise<Context>
analytics.group(groupId, traits, options, callback)
Associate user with group.
Parameters:
groupId(string, required): Group identifiertraits(object, optional): Group attributesoptions(object, optional): Additional optionscallback(function, optional): Callback function
Returns: Promise<Context>
analytics.alias(userId, previousId, options, callback)
Merge user identities.
Parameters:
userId(string, required): New user IDpreviousId(string, optional): Previous user IDoptions(object, optional): Additional optionscallback(function, optional): Callback function
Returns: Promise<Context>
analytics.reset()
Clear user identification.
Returns: Promise<Context>
analytics.ready(callback)
Execute callback when SDK is ready.
Parameters:
callback(function, required): Callback function
Returns: Promise<void>
Node.js SDK
new Analytics(settings)
Create analytics instance.
Parameters:
settings(object)writeKey(string, required): Your Zixflow API keyhost(string, optional): API host URLpath(string, optional): API pathmaxEventsInBatch(number, optional): Max events per batch (default: 15)flushInterval(number, optional): Flush interval in ms (default: 10000)maxRetries(number, optional): Max retry attempts (default: 3)httpRequestTimeout(number, optional): Request timeout in ms
Returns: Analytics instance
analytics.identify(params, callback)
Identify a user.
Parameters:
params(object)userId(string, optional): User identifieranonymousId(string, optional): Anonymous identifiertraits(object, optional): User attributescontext(object, optional): Context informationtimestamp(Date, optional): Event timestampintegrations(object, optional): Integration settingsmessageId(string, optional): Message ID
callback(function, optional): Callback function
Returns: void
analytics.track(params, callback)
Track an event.
Parameters:
params(object)userId(string, optional): User identifieranonymousId(string, optional): Anonymous identifierevent(string, required): Event nameproperties(object, optional): Event propertiescontext(object, optional): Context informationtimestamp(Date, optional): Event timestampintegrations(object, optional): Integration settingsmessageId(string, optional): Message ID
callback(function, optional): Callback function
Returns: void
analytics.page(params, callback)
Track a page view.
Parameters:
params(object)userId(string, optional): User identifieranonymousId(string, optional): Anonymous identifiercategory(string, optional): Page categoryname(string, optional): Page nameproperties(object, optional): Page propertiescontext(object, optional): Context informationtimestamp(Date, optional): Event timestamp
callback(function, optional): Callback function
Returns: void
analytics.screen(params, callback)
Track a screen view.
Parameters: Same as page()
Returns: void
analytics.group(params, callback)
Associate user with group.
Parameters:
params(object)userId(string, optional): User identifieranonymousId(string, optional): Anonymous identifiergroupId(string, required): Group identifiertraits(object, optional): Group attributescontext(object, optional): Context informationtimestamp(Date, optional): Event timestamp
callback(function, optional): Callback function
Returns: void
analytics.alias(params, callback)
Merge user identities.
Parameters:
params(object)userId(string, required): New user IDpreviousId(string, required): Previous user IDcontext(object, optional): Context informationtimestamp(Date, optional): Event timestamp
callback(function, optional): Callback function
Returns: void
analytics.closeAndFlush(options)
Flush all events and close.
Parameters:
options(object, optional)timeout(number, optional): Maximum wait time in ms
Returns: Promise<void>
