Skip to content

Web push

Web push reaches users whose app is closed. Your web app registers the browser with ChatPushClient, and the browser shows Chat’s notifications through a service worker you host.

Let a signed-in user turn notifications on and off, and open your app when they click one.

  • A getAccessToken that returns the user’s Chat token. See Get the user’s Chat token.
  • A secure origin (HTTPS, or localhost) and a browser with service workers and the Push API.
  • Chat web push enabled for your deployment. Ask support if registration fails with an empty key error. See Support.

Copy push-sw-template.js from the package into your static root as push-sw.js. It must be served from the root of your site, as JavaScript, so its scope covers your whole app.

Terminal window
cp node_modules/@central-code-station/ccs-chat-notifications-client/push-sw-template.js public/push-sw.js

The template handles two events. On push it shows a notification from the payload ({ notification: { title, body }, data }). On notificationclick it closes the notification, focuses an open window of your app, or opens / when there is none. The click does not navigate to the conversation. The data field carries tenant_id, application_id, conversation_id and message_id, so edit notificationclick if you want a deep link. See Push payloads.

To use another file name, pass it as serviceWorkerUrl.

The first permission prompt needs a user gesture. Browsers silently withhold it when you ask on page load. Call register() from a click handler.

push.ts
import { ChatPushClient } from '@central-code-station/ccs-chat-notifications-client/push';
import { context } from './fe-guide-context';
// Push devices belong to the tenant, so there is no applicationId.
const push = new ChatPushClient({
chatApiUrl: context.chatApiUrl,
tenantId: context.tenantId,
getAccessToken: context.getAccessToken,
// serviceWorkerUrl: '/push-sw.js', // the default
});
const enable = document.querySelector<HTMLButtonElement>('#enable-notifications');
const disable = document.querySelector<HTMLButtonElement>('#disable-notifications');
if (!push.isSupported()) enable?.remove(); // no service worker or Push API here
// The first permission prompt needs a real click. Do not call register() on page load.
enable?.addEventListener('click', async () => {
const result = await push.register(); // never rejects
if (!result.success) console.warn('Web push is not available', result.error);
});
disable?.addEventListener('click', () => void push.unregister());

isSupported() tells you whether this browser can do web push, so check it before you show the button. register() and unregister() never reject. They return { success, error? } and log failures to the console, so push can never break the rest of your app.

register() registers the service worker, fetches Chat’s public key, subscribes through the browser’s Push API and sends the subscription to Chat. A device belongs to the tenant, so one registration serves every application of that tenant. Call unregister() when the user turns notifications off or signs out. It works even if register() did not run in this session.

Chat sends web push only to members who are offline, meaning no recent presence heartbeat. In DIRECT and GROUP conversations every other member is a candidate. In channels only mentioned members are. See Typing, read state and presence.

Register, then open DevTools, Application, Service Workers. push-sw.js shows as activated. Close the tab, or stop heartbeats, and send a message from another user. A notification appears. Clicking it focuses or opens your app.

You see Cause Fix
No permission prompt register() ran without a click, or the user already blocked notifications. Call it from a click. A blocked site must be unblocked in the browser’s site settings.
register() returns success: false Permission denied, unsupported browser, or a network or token failure. Read error. Show a message, then retry from a click.
Service worker fails to register /push-sw.js returns your app’s HTML or a 404. Serve the file as JavaScript from the site root.
Push works with the tab open but not closed The subscription was not saved, for example the token expired during register(). Call register() again.

For more symptoms see Troubleshooting.