notiflowsDocs
Channels & ProvidersWeb Push

Overview

Web push notifications using the Web Push standard (VAPID)

Web Push enables you to send push notifications directly to web browsers using the Web Push Protocol with VAPID authentication, even when users aren't actively on your site.

Supported Browsers

  • Chrome
  • Firefox
  • Safari
  • Edge

Setup

1. Generate VAPID Keys

Generate a VAPID key pair for your application. You can use any standard tool:

npx web-push generate-vapid-keys

This produces a public key and a private key. Keep the private key secret.

2. Create a Web Push Channel

In your Notiflows dashboard, go to Channels and create a new Web Push channel with the Web Push (VAPID) provider. You'll need to provide:

FieldDescription
VAPID Public KeyThe base64url-encoded public key from step 1
VAPID Private KeyThe base64url-encoded private key from step 1
VAPID SubjectA contact URI — either mailto:admin@yourapp.com or https://yourapp.com

The VAPID subject identifies your application server to push services. It must be a valid mailto: address or https:// URL.

3. Add a Service Worker

Your application needs a service worker to handle incoming push events and display notifications. Create a sw.js file at the root of your site:

self.addEventListener('push', (event) => {
  const data = event.data?.json() ?? {};

  const title = data.title || 'New Notification';
  const options = {
    body: data.body || '',
    icon: data.icon,
    image: data.image,
    data: { actionUrl: data.action_url },
  };

  event.waitUntil(self.registration.showNotification(title, options));
});

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

4. Register Push Subscriptions

On the client side, use the browser's Push API to subscribe users and send the subscription to Notiflows via the Channel Subscriptions API:

// Register the service worker
const registration = await navigator.serviceWorker.register('/sw.js');
await navigator.serviceWorker.ready;

// Request permission and subscribe
const subscription = await registration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: '<your-vapid-public-key>',
});

// Send the subscription to Notiflows via the User API
const { endpoint, keys } = subscription.toJSON();

await fetch('https://api.notiflows.com/user/v1/channels/<channel-id>/subscriptions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-notiflows-api-key': '<your-public-api-key>',
    'x-notiflows-user-key': '<user-jwt-token>',
  },
  body: JSON.stringify({
    identifier: endpoint,
    settings: {
      endpoint,
      p256dh: keys.p256dh,
      auth: keys.auth,
    },
  }),
});

The applicationServerKey must be the same VAPID public key configured on your channel.

Users can subscribe from multiple browsers or devices. Notiflows deduplicates by identifier and delivers to all registered subscriptions. It is safe to call this on every page load — if the subscription already exists, the settings are updated in place.

5. Handle Unsubscription

When a user opts out of push notifications, unsubscribe them from the browser and remove the subscription from Notiflows:

const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.getSubscription();

if (subscription) {
  const { endpoint } = subscription.toJSON();

  // Remove from Notiflows
  await fetch('https://api.notiflows.com/user/v1/channels/<channel-id>/subscriptions', {
    method: 'DELETE',
    headers: {
      'Content-Type': 'application/json',
      'x-notiflows-api-key': '<your-public-api-key>',
      'x-notiflows-user-key': '<user-jwt-token>',
    },
    body: JSON.stringify({ identifier: endpoint }),
  });

  // Unsubscribe the browser
  await subscription.unsubscribe();
}

Templates

Web push templates support the following fields:

FieldRequiredFormatDescription
TitleYesPlaintextThe notification heading
BodyYesPlaintextThe notification body text
Icon URLNoURLSmall image displayed next to the title (typically your app logo)
Image URLNoURLLarge image displayed in the notification body
Action URLNoURLURL opened when the user clicks the notification

Title and body are always plaintext — this is a browser limitation. All fields support Liquid templating for dynamic content.

Example:

FieldValue
TitleNew message from {{ actor.first_name }}
Body{{ data.message_preview }}
Icon URLhttps://yourapp.com/icon.png
Action URLhttps://yourapp.com/messages/{{ data.message_id }}

On this page