> For the complete documentation index, see [llms.txt](https://docs.baas.sh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.baas.sh/baas-sdk/integrations/push-notifications.md).

# Push notifications

Set up Firebase Web Push so your BaaS automations can send notifications to your users.

An [automation](/baas-console/automations/actions/notification.md) — a rule that runs in your project when something happens on-chain — can push a notification to your users.

Set up the Firebase Web Push client, then register the token with `baas.user.addFcmToken`. This is the standard Firebase Web Push setup, with one change: the token goes to `addFcmToken` instead of your own backend.

{% hint style="info" %}
You need a Firebase project with a **Web app** and a **Web Push (VAPID) key**. Registering the Web app gives you the config used below (`apiKey`, `projectId`, `messagingSenderId`, `appId`), and you create the VAPID key under **Project settings → Cloud Messaging → Web Push certificates → Generate key pair**. Firebase's [Set up a JavaScript client](https://firebase.google.com/docs/cloud-messaging/js/client) walks through both. Push works on a secure origin (HTTPS), and `localhost` is exempt, so local development just works.
{% endhint %}

## Receive notifications

Install the Firebase JS SDK alongside `@baas.sh/sdk`:

```bash
npm install firebase
```

{% stepper %}
{% step %}

### Add the messaging service worker

Firebase delivers push through a service worker.

{% code title="public/firebase-messaging-sw\.js" %}

```js
// Pin these compat imports to the same version as your `firebase` package (12.15.0 shown).
importScripts('https://www.gstatic.com/firebasejs/12.15.0/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/12.15.0/firebase-messaging-compat.js');

// A service worker can't read your app's env, so its Firebase config is inlined here.
// This config is public by design, so it is safe to ship.
firebase.initializeApp({
  apiKey:            'AIza…',
  projectId:         'my-app',
  messagingSenderId: '1234567890',
  appId:             '1:1234567890:web:abc…',
});

// BaaS sends a notification payload, so the browser shows it automatically in the background.
// This worker only needs firebase.messaging(). (onBackgroundMessage is for data-only messages
// or custom display; adding it here would show each notification twice.)
firebase.messaging();
```

{% endcode %}

Name the file `firebase-messaging-sw.js` and serve it from your site root (the `public/` folder in Vite, CRA, Next.js, and most setups).
{% endstep %}

{% step %}

### Register a token

On a user click, request permission, get a token, and register it with `baas.user.addFcmToken`.

{% code title="push.ts" %}

```ts
import { initializeApp } from 'firebase/app';
import { getMessaging, getToken, isSupported } from 'firebase/messaging';
import { baas } from './baas';

const app = initializeApp({
  apiKey:            'AIza…',
  projectId:         'my-app',
  messagingSenderId: '1234567890',
  appId:             '1:1234567890:web:abc…',
});
const VAPID_KEY = 'B…'; // Web Push certificate key pair (public)

// Call this from a click handler: browsers only grant permission on a user gesture.
export async function enablePush(): Promise<void> {
  if (!('Notification' in window)) return;

  if ((await Notification.requestPermission()) !== 'granted') return;
  // Check support after the prompt: requestPermission() must be the first await after the click.
  if (!(await isSupported())) return;

  // Register the service worker at the site root so it controls the page,
  // then hand it to getToken as the push target.
  const registration = await navigator.serviceWorker.register('/firebase-messaging-sw.js');
  const token = await getToken(getMessaging(app), {
    vapidKey: VAPID_KEY,
    serviceWorkerRegistration: registration,
  });

  await baas.user.addFcmToken(token); // now an automation can reach this device
}
```

{% endcode %}

{% hint style="warning" %}
Trigger `enablePush()` from a **user click**, not automatically on page load (a React `useEffect` on mount is just one example). Browsers grant notification permission only in response to a user gesture, so `requestPermission()` must be the first `await` after the click.
{% endhint %}

`enablePush()` resolves without error if the browser can't support push or the user declines. A `getToken` or `addFcmToken` failure throws, so catch it where you call `enablePush()` to show a retry (see [Error handling](/baas-sdk/resources/error-handling.md)).
{% endstep %}

{% step %}

### Show notifications while the tab is open

While a tab is open, show notifications yourself. Add a foreground listener to the same `push.ts`, reusing the `app` you created in the previous step:

{% code title="push.ts" %}

```ts
import { onMessage } from 'firebase/messaging';

// FCM auto-displays notifications only in the background. While a tab is open, show
// them yourself. The `listening` guard attaches the onMessage listener only once,
// so calling this more than once is safe.
let listening = false;
export async function listenForForegroundMessages(): Promise<void> {
  if (listening || Notification.permission !== 'granted') return;
  listening = true;

  const registration = await navigator.serviceWorker.register('/firebase-messaging-sw.js');
  onMessage(getMessaging(app), (payload) => {
    registration.showNotification(payload.notification?.title ?? '', {
      body: payload.notification?.body,
      data: payload.data,
    });
  });
}
```

{% endcode %}

Call `listenForForegroundMessages()` once at startup, after permission is granted.
{% endstep %}
{% endstepper %}

{% hint style="info" %}
Notifications appear in the operating system's notification center, not inside your page. That's the standard web push experience.
{% endhint %}

{% hint style="info" %}
BaaS delivers to FCM registration tokens, which is what `getToken` returns. Installation IDs are not a delivery target.
{% endhint %}

## Deliver from an automation

The sending side lives in the BaaS Console: a [**Notification** action](/baas-console/automations/actions/notification.md) on an automation pushes to the tokens you registered. Its delivery runs on your Firebase project's credentials, configured once on the Notification plugin; [Action Plugins](/baas-console/action-plugins.md#notification) walks through that setup. Use the same Firebase project there as in the web config above.

## Next

* [Manage user](/baas-sdk/manage-user.md) — add and remove FCM tokens on the profile.
* Firebase [Set up a JavaScript client](https://firebase.google.com/docs/cloud-messaging/js/client) — create the project, web app, and VAPID key.
* [Error handling](/baas-sdk/resources/error-handling.md) — handle `NOT_SIGNED_IN` and API errors from `addFcmToken`.
