notiflowsDocs
SDKsClient-side

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/client

Quick 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
const welcomeNotifs = await feed.getEntries({
  notiflowId: 'welcome-flow',
});

// Filter by topic
const orderNotifs = await feed.getEntries({
  topic: 'order:123',
});

// Include archived
const withArchived = await feed.getEntries({
  includeArchived: true,
});

Real-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;
  data: {
    body: string;
    actionUrl?: string;
  };
  actor?: {
    id: string;
    email?: string;
    first_name?: string;
    last_name?: string;
    avatar?: string;
  };
  seen_at: string | null;
  read_at: string | null;
  clicked_at: string | null;
  archived_at: string | null;
  created_at: string;
}

FeedEntryCollection

interface FeedEntryCollection {
  items: FeedEntry[];
  total_unread: number;
  total_unseen: number;
  has_more_after: boolean;
  after: string | null;
}

FeedEntryStatus

enum FeedEntryStatus {
  Unseen = 'unseen',
  Unread = 'unread',
  Read = 'read',
  Archived = 'archived',
}

Preferences

interface Preferences {
  notiflows: {
    [notiflowId: string]: {
      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

ErrorHTTP StatusDescription
BadRequestError400Invalid request parameters
UnauthenticatedError401Invalid or missing authentication
ForbiddenError403Insufficient permissions
NotFoundError404Resource not found
ConflictError409Resource state conflict
ValidationFailedError422Validation errors on request body
RateLimitedError429Too many requests
InternalError500Server error
ServiceUnavailableError503Service temporarily unavailable

TypeScript

The SDK is written in TypeScript and exports all types:

import type {
  FeedEntry,
  FeedEntryCollection,
  FeedEntryStatus,
  GetEntriesParams,
  EntryStateUpdate,
  Preferences,
  NotiflowsOptions,
} from '@notiflows/client';

Browser Support

The SDK supports all modern browsers (ES2022+) and Node.js 18+.

For older browsers, you may need to polyfill:

  • Promise
  • fetch (if using Node.js < 18)

Next Steps

On this page