# How to Set Up Push Notifications for Chat and System Events in AnythingLLM

> Master AnythingLLM push notifications for chat and system events. Learn easy setup for real-time alerts and improved communication with this guide.

- Repository: [Mintplex Labs/anything-llm](https://github.com/Mintplex-Labs/anything-llm)
- Tags: how-to-guide
- Published: 2026-03-07

---

**AnythingLLM supports real-time Web Push notifications for chat messages and system alerts through a built-in service that automatically generates VAPID keys, manages browser subscriptions via a service worker, and delivers targeted payloads to single-user or multi-user environments.**

The `Mintplex-Labs/anything-llm` repository includes a complete push notification infrastructure that works out of the box for both self-hosted single-user instances and multi-user deployments. This guide covers the server configuration, client integration, and programmatic usage of the `web-push` implementation found in the core source code.

## How Push Notifications Work in AnythingLLM

The architecture consists of three coordinated layers that handle key generation, subscription management, and message delivery.

- **Server Layer**: Located in [`server/utils/PushNotifications/index.js`](https://github.com/Mintplex-Labs/anything-llm/blob/main/server/utils/PushNotifications/index.js), this manages **VAPID key generation**, subscription persistence (file-based for single-user or database for multi-user), and the `web-push` library integration.
- **API Layer**: Defined in [`server/endpoints/webPush.js`](https://github.com/Mintplex-Labs/anything-llm/blob/main/server/endpoints/webPush.js), this exposes the public key endpoint and subscription registration route.
- **Client Layer**: The React hook in [`frontend/src/hooks/useWebPushNotifications.js`](https://github.com/Mintplex-Labs/anything-llm/blob/main/frontend/src/hooks/useWebPushNotifications.js) handles permission requests, service worker registration, and subscription posting, while [`frontend/public/service-workers/push-notifications.js`](https://github.com/Mintplex-Labs/anything-llm/blob/main/frontend/public/service-workers/push-notifications.js) displays native browser notifications.

The boot process in [`server/utils/boot/index.js`](https://github.com/Mintplex-Labs/anything-llm/blob/main/server/utils/boot/index.js) automatically initializes the service. Both `bootHTTP` (line 39) and `bootSSL` (line 71) call `await PushNotifications.setupPushNotificationService()` to ensure VAPID keys exist and subscriptions load on startup.

## Server-Side Configuration

### Automatic VAPID Key Generation

You do not need to manually create VAPID keys. During the first server start, `setupPushNotificationService()` checks for [`storage/push-notifications/vapid-keys.json`](https://github.com/Mintplex-Labs/anything-llm/blob/main/storage/push-notifications/vapid-keys.json). If absent, it executes:

```javascript
const vapidKeys = webpush.generateVAPIDKeys();
fs.writeFileSync(
  path.resolve(this.storagePath, `vapid-keys.json`), 
  JSON.stringify(vapidKeys, null, 2)
);

```

These keys persist to the storage directory and are reused on subsequent boots.

### Storage Modes

- **Single-User Mode**: Subscriptions save to [`storage/push-notifications/primary-subscription.json`](https://github.com/Mintplex-Labs/anything-llm/blob/main/storage/push-notifications/primary-subscription.json) using the key `"primary"`.
- **Multi-User Mode**: Subscriptions store in the `users` table under individual user records, allowing targeted notifications per user ID.

## API Endpoints for Web Push

The server exposes two REST endpoints in [`server/endpoints/webPush.js`](https://github.com/Mintplex-Labs/anything-llm/blob/main/server/endpoints/webPush.js):

| Method | Endpoint | Purpose |
|--------|----------|---------|
| **GET** | `/web-push/pubkey` | Returns the VAPID public key required for browser subscription |
| **POST** | `/web-push/subscribe` | Accepts and stores the Web Push subscription object |

The client automatically interacts with these endpoints through the `useWebPushNotifications` hook.

## Client-Side Integration

### Implementing the React Hook

Import and invoke the hook in your root component (e.g., [`frontend/src/App.jsx`](https://github.com/Mintplex-Labs/anything-llm/blob/main/frontend/src/App.jsx)) to enable automatic subscription:

```tsx
import useWebPushNotifications from "@/hooks/useWebPushNotifications";

function App() {
  useWebPushNotifications(); // Auto-registers on mount
  
  return (
    <div>{/* Your application UI */}</div>
  );
}

export default App;

```

### Subscription Flow

The `subscribeToPushNotifications()` function (lines 24–100) performs the following sequence:

1. Validates browser support for Service Workers and the Push API
2. Requests `Notification` permission from the user
3. Fetches the VAPID public key from `/web-push/pubkey`
4. Registers the service worker at `/service-workers/push-notifications.js?v=${SW_VERSION}` (current version `"1.0.0"`)
5. Calls `pushManager.subscribe()` with the server key
6. POSTs the resulting subscription to `/web-push/subscribe`

### Service Worker Behavior

The worker at [`frontend/public/service-workers/push-notifications.js`](https://github.com/Mintplex-Labs/anything-llm/blob/main/frontend/public/service-workers/push-notifications.js) listens for push events:

```javascript
self.addEventListener('push', event => {
  const payload = event.data?.json();
  if (!payload) return;
  
  self.registration.showNotification(payload.title || 'AnythingLLM', {
    ...payload,
    icon: '/favicon.png',
  });
});

self.addEventListener('notificationclick', event => {
  event.notification.close();
  const { onClickUrl } = event.notification.data || {};
  if (onClickUrl) event.waitUntil(clients.openWindow(onClickUrl));
});

```

Clicking a notification opens the URL specified in `data.onClickUrl`.

## Sending Notifications Programmatically

Use the exported `pushNotificationService` to deliver messages from anywhere in your server code:

```javascript
const { pushNotificationService } = require("./utils/PushNotifications");

const payload = {
  title: "New chat message",
  body: "Alice replied in thread #1234",
  data: { onClickUrl: "/chat/1234" },
  // Optional: actions, image, badge
};

// Single-user mode
pushNotificationService.sendNotification({
  to: "primary",
  payload,
});

// Multi-user mode - target specific user
pushNotificationService.sendNotification({
  to: userId, // Database user ID
  payload,
});

```

The `sendNotification` method (lines 175–186) validates the subscription, logs the action, and delegates to the `web-push` library for delivery.

### Example: Chat Message Notification

Create a utility function to notify users of new messages:

```javascript
// server/utils/notifyChat.js
const { pushNotificationService } = require("./PushNotifications");

async function notifyChatMessage({ chatId, messageText, authorName, recipientUserId }) {
  const payload = {
    title: `New message from ${authorName}`,
    body: messageText.substring(0, 100), // Truncate long messages
    data: { onClickUrl: `/workspace/chat/${chatId}` },
  };

  // Target specific user in multi-user instances
  await pushNotificationService.sendNotification({
    to: recipientUserId || "primary",
    payload,
  });
}

module.exports = { notifyChatMessage };

```

## Summary

- **VAPID keys generate automatically** on first boot and store in [`storage/push-notifications/vapid-keys.json`](https://github.com/Mintplex-Labs/anything-llm/blob/main/storage/push-notifications/vapid-keys.json)
- **Two API endpoints** (`/web-push/pubkey` and `/web-push/subscribe`) handle the Web Push protocol handshake
- **Single-user** subscriptions persist to JSON files; **multi-user** subscriptions save to the database `users` table
- **Client integration** requires only importing `useWebPushNotifications` in your root React component
- **Service worker** at [`/service-workers/push-notifications.js`](https://github.com/Mintplex-Labs/anything-llm/blob/main//service-workers/push-notifications.js) displays notifications and handles click events via `notificationclick`
- **Server-side sending** uses `pushNotificationService.sendNotification({ to, payload })` with target `"primary"` or a specific user ID

## Frequently Asked Questions

### Do I need to manually generate VAPID keys for AnythingLLM push notifications?

No. The server automatically generates and persists VAPID keys during the boot sequence when you call `setupPushNotificationService()`. The keys are stored in [`storage/push-notifications/vapid-keys.json`](https://github.com/Mintplex-Labs/anything-llm/blob/main/storage/push-notifications/vapid-keys.json) and reused on subsequent starts.

### How do I target specific users in multi-user mode?

In multi-user deployments, store the subscription under the user's database record by ensuring the user is authenticated when calling `/web-push/subscribe`. Then use `pushNotificationService.sendNotification({ to: userId, payload })` where `userId` matches the database record identifier.

### Where are subscriptions stored in single-user mode?

Single-user instances save subscriptions to [`storage/push-notifications/primary-subscription.json`](https://github.com/Mintplex-Labs/anything-llm/blob/main/storage/push-notifications/primary-subscription.json). The system uses the constant string `"primary"` as the identifier in the `to` parameter when sending notifications.

### What triggers the service worker to update?

The service worker URL includes a version query parameter (`?v=1.0.0`). Bump the `SW_VERSION` constant in the client code to force browsers to register the new worker when the application deploys.

### Can I customize the notification appearance and behavior?

Yes. The payload object accepts standard Web Notification API properties including `title`, `body`, `icon`, `image`, `badge`, `actions`, and `data`. The service worker passes these directly to `showNotification()`, and the `notificationclick` handler respects `data.onClickUrl` for navigation.