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.
Prerequisites
Section titled “Prerequisites”- A
getAccessTokenthat 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.
1. Host the service worker
Section titled “1. Host the service worker”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.
cp node_modules/@central-code-station/ccs-chat-notifications-client/push-sw-template.js public/push-sw.jsThe 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.
2. Register from a real click
Section titled “2. Register from a real click”The first permission prompt needs a user gesture. Browsers silently withhold it when you ask on page load. Call register() from a click handler.
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());import { usePushRegistration } from '@central-code-station/ccs-chat-notifications-client/push/react';import { context } from './fe-guide-context';
export function NotificationsToggle() { const { isSupported, status, register, unregister } = usePushRegistration({ chatApiUrl: context.chatApiUrl, tenantId: context.tenantId, getAccessToken: context.getAccessToken, });
if (!isSupported) return null; const busy = status === 'registering' || status === 'unregistering'; // status starts as 'idle' and only tracks calls made from this hook. return ( <button disabled={busy} onClick={() => (status === 'registered' ? unregister() : register())}> {status === 'registered' ? 'Turn off notifications' : 'Turn on notifications'} {status === 'error' ? ' (not available)' : ''} </button> );}There is no Angular wrapper. Inject ChatPushClient into your own service and call register() and unregister() from a click handler, as in the Plain tab.
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.
3. Know who gets a push
Section titled “3. Know who gets a push”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.
Verify
Section titled “Verify”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.
Troubleshooting
Section titled “Troubleshooting”| 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.