JavaScript
Notiflows JavaScript SDK for client-side notification integration
The JavaScript SDK (@notiflows/client) provides a client for interacting with the Notiflows User API. Use it to fetch notifications, manage notification state, and subscribe to real-time updates.
For React applications, we recommend using the React SDK which provides hooks and pre-built components on top of this client.
Installation
npm install @notiflows/client
# or
pnpm add @notiflows/client
# or
yarn add @notiflows/clientQuick Start
import { Notiflows } from '@notiflows/client';
// Initialize the client
const client = new Notiflows({
apiKey: 'pk_your_public_key',
userId: 'user_123',
userKey: 'jwt_token_from_backend',
});
// Get the notification feed
const feed = client.feed({ channelId: 'your-channel-id' });
// Fetch notifications
const collection = await feed.getEntries();
console.log(collection.items, collection.total_unread);
// Subscribe to real-time updates
feed.onDelivery = (entry) => {
console.log('New notification:', entry);
};
feed.subscribeToRealtimeNotifications();
// Mark as read
await feed.markAsRead('entry_id');Configuration
const client = new Notiflows({
// Required
apiKey: string, // Your public API key (starts with pk_)
userId: string, // The user's external ID
userKey: string, // JWT token signed with your signing key
// Optional
apiUrl?: string, // Custom API URL (default: https://api.notiflows.com/user/v1)
wsUrl?: string, // Custom WebSocket URL (default: wss://api.notiflows.com/ws/v1)
});The userKey must be generated on your backend using your Application Signing Key. Never expose your signing key in client-side code. See Generating User Tokens for instructions.
Feed
The Feed resource manages notification entries for a specific channel.
Creating a Feed Instance
const feed = client.feed({
channelId: 'your-in-app-channel-id',
});Fetching Entries
import { FeedEntryStatus } from '@notiflows/client';
// Get all notifications
const collection = await feed.getEntries();
// Access the data
const { items, total_unread, total_unseen } = collection;
// With filters
const unreadOnly = await feed.getEntries({
status: FeedEntryStatus.Unread,
limit: 20,
});
// With pagination
const collection = await feed.getEntries({ limit: 10 });
if (collection.has_more_after) {
const nextPage = await feed.getEntries({
limit: 10,
after: collection.after,
});
}
// Filter by notiflow handle
const orderNotifs = await feed.getEntries({
notiflow: 'order-updates',
});
// Filter by topic
const topicNotifs = await feed.getEntries({
topic: 'order:123',
});
// Include archived entries
const withArchived = await feed.getEntries({
archived: true,
});Feed Settings
Fetch feed settings (e.g. whether branding is required):
const settings = await feed.getSettings();
console.log(settings.branding_required); // true on free planReal-time Updates
Subscribe to receive notifications as they're delivered:
// Set up handlers before subscribing
feed.onDelivery = (entry) => {
console.log('New notification:', entry);
// Add to your UI, play a sound, etc.
};
// Start listening for real-time updates
feed.subscribeToRealtimeNotifications();
// Stop listening when done
feed.stop();Updating Entry State
Mark notifications as seen, read, clicked, or archived:
// Single entry updates
await feed.markAsSeen(entryId);
await feed.markAsRead(entryId);
await feed.markAsClicked(entryId);
await feed.markAsArchived(entryId);
await feed.markAsUnarchived(entryId);
// Batch updates
await feed.batchMarkAsSeen([entryId1, entryId2]);
await feed.batchMarkAsRead([entryId1, entryId2]);
await feed.batchMarkAsClicked([entryId1, entryId2]);
await feed.batchMarkAsArchived([entryId1, entryId2]);
await feed.batchMarkAsUnarchived([entryId1, entryId2]);
// Generic update with multiple fields
await feed.updateEntry(entryId, {
read: true,
seen: true,
});
await feed.batchUpdateEntries([entryId1, entryId2], {
archived: true,
});User Preferences
Manage user notification preferences:
const preferences = client.userPreferences();
// Get current preferences
const prefs = await preferences.get();
console.log(prefs.notiflows); // Per-notiflow preferences
// Update preferences
await preferences.update({
notiflows: {
'marketing-emails': {
enabled: false,
},
},
});Types
FeedEntry
interface FeedEntry {
id: string;
notiflow_handle: string;
data: {
body: string;
action_type: 'default' | 'single' | 'multi';
action_url?: string;
primary_action?: { label: string; url: string };
secondary_action?: { label: string; url: string };
};
actor?: {
id: string;
external_id: string;
first_name?: string;
last_name?: string;
avatar?: string;
email?: string;
};
topic?: string;
status: FeedEntryStatus;
sent_at?: string;
seen_at?: string;
read_at?: string;
clicked_at?: string;
archived_at?: string;
created_at: string;
}FeedEntryCollection
interface FeedEntryCollection {
items: FeedEntry[];
total_unread: number;
total_unseen: number;
has_more_after: boolean;
after: string | null;
}FeedSettings
interface FeedSettings {
branding_required: boolean;
}FeedEntryStatus
enum FeedEntryStatus {
Unseen = 'unseen',
Seen = 'seen',
Unread = 'unread',
Read = 'read',
Archived = 'archived',
}Preferences
interface Preferences {
notiflows: {
[notiflowHandle: string]: { // keyed by notiflow handle (e.g. "order-updates")
name: string;
enabled: boolean;
};
};
}Error Handling
The SDK provides typed errors for different API responses:
import {
isApiError,
BadRequestError,
UnauthenticatedError,
ForbiddenError,
NotFoundError,
RateLimitedError,
ValidationFailedError,
ConflictError,
InternalError,
ServiceUnavailableError,
} from '@notiflows/client';
try {
await feed.getEntries();
} catch (error) {
if (isApiError(error)) {
console.log(error.code); // Error code
console.log(error.message); // Error message
console.log(error.details); // Additional details
if (error instanceof UnauthenticatedError) {
// Redirect to login or refresh token
} else if (error instanceof RateLimitedError) {
// Back off and retry
} else if (error instanceof ValidationFailedError) {
// Check error.details for field-level errors
}
}
}Error Types
| Error | HTTP Status | Description |
|---|---|---|
BadRequestError | 400 | Invalid request parameters |
UnauthenticatedError | 401 | Invalid or missing authentication |
ForbiddenError | 403 | Insufficient permissions |
NotFoundError | 404 | Resource not found |
ConflictError | 409 | Resource state conflict |
ValidationFailedError | 422 | Validation errors on request body |
RateLimitedError | 429 | Too many requests |
InternalError | 500 | Server error |
ServiceUnavailableError | 503 | Service temporarily unavailable |
TypeScript
The SDK is written in TypeScript and exports all types:
import type {
FeedEntry,
FeedEntryCollection,
FeedEntryStatus,
FeedSettings,
GetEntriesParams,
EntryStateUpdate,
Preferences,
NotiflowsOptions,
} from '@notiflows/client';Allowed Origins
Before using the SDK in a browser, you must add your domain to the project's allowed origins list. Without this, the browser will block all API requests due to CORS.
See Allowed Origins for setup instructions.
Browser Support
The SDK supports all modern browsers (ES2022+) and Node.js 18+.
For older browsers, you may need to polyfill:
Promisefetch(if using Node.js < 18)
Next Steps
- React SDK - Pre-built components and hooks for React
- Authentication - Learn about user token generation
- API Reference - Full User API documentation