← All guides

Push notifications

Push notifications integration guide

Send notifications to iOS, Android and browsers with Hibira Push. The API is Beams-compatible: the official Beams server SDKs work once they point at your endpoint, and our client SDKs are forks of the Beams ones with the host made configurable.

Sign in to see these snippets with your own endpoint, and the app page fills in your instance ID. Sign in

How it works

  • Turn on push notifications on an app’s page in the portal. You get an instance ID, a secret key and an endpoint.
  • Each device or browser registers with a client SDK and subscribes to interests, topics such as news or order-1024. Your server publishes to interests, or to user IDs once a device is linked to a signed-in user.
  • The instance ID and endpoint go into your apps. The secret key can publish to every device: keep it on your server, in the HIBIRA_PUSH_SECRET_KEY environment variable, and never in an app or a repository.
  • Push is included in every plan. The plan sets how many devices and browsers can register and how fast you can publish.

1. Upload your credentials

Apple (APNs)
In Apple Developer, open Certificates, Identifiers & Profiles › Keys, create a key with Apple Push Notifications service (APNs) and download the .p8 file. On the app page, under Push notifications › Apple (APNs), enter the Key ID, your Team ID and the app’s bundle ID, pick the environment and upload the .p8 file. Choose Development (sandbox) for builds run from Xcode, and Production for TestFlight and App Store builds.
Android (FCM)
In the Firebase console, open Project settings › Service accounts and click Generate new private key. Upload the JSON file under Push notifications › Android (FCM), and add the same project’s google-services.json to your Android app.
Web Push
Nothing to upload: the VAPID keys are created when you turn on push. Your site has to be served over HTTPS (localhost works while developing).

Keys and service accounts are stored encrypted and are never shown again.

2. Register devices and browsers

Client SDK

Platform

Chrome, Edge, Firefox, Opera and Safari 16.4+ with standard Web Push and a service worker. @hibira-io/push-web ↗

  1. Install

    npm install @hibira-io/push-web
  2. Add the service worker

    // public/service-worker.js, served at https://your-site/service-worker.js
    importScripts('https://cdn.jsdelivr.net/npm/@hibira-io/push-web@1/dist/service-worker.js');
    self.HibiraPush.endpoint = 'https://ws.hibira.io';
  3. Register the browser

    import { Client } from '@hibira-io/push-web';
    
    const push = new Client({
      instanceId: 'YOUR_INSTANCE_ID',
      endpoint: 'https://ws.hibira.io',
    });
    
    // Browsers only show the permission prompt after a click or tap.
    document.querySelector('#enable-notifications').addEventListener('click', async () => {
      await push.start();
      await push.addDeviceInterest('hello');
    });
  4. Link the signed-in user

    import { TokenProvider } from '@hibira-io/push-web';
    
    // After sign-in. Your backend answers GET /push/user-token?user_id=... (see the server guide).
    await push.setUserId(currentUser.id, new TokenProvider({ url: '/push/user-token' }));
    
    // On sign-out, so the next person on this browser doesn't get their notifications.
    await push.stop();
Agent prompt for Web

Paste this into Claude Code, Cursor, GitHub Copilot or another AI coding agent. It carries the instance ID and endpoint; the secret key stays an environment variable.

Add Hibira push notifications to this Web project. Hibira Push sends to iOS (APNs), Android (FCM) and browsers (Web Push) through a Beams-compatible API, so it works with the Hibira client SDK (here: @hibira-io/push-web, https://github.com/hibira-io/hibira-push-web).

Connection details:
- Instance ID: YOUR_INSTANCE_ID
- Endpoint: https://ws.hibira.io
- No secret key: it belongs on the server, never in this app

What to do:
1. Look at the project first (framework version, package manager, config and env conventions, where similar integrations live) and follow what it already does.
2. Install @hibira-io/push-web (it replaces @pusher/push-notifications-web; the API is the same apart from the import and the endpoint option).
3. Serve service-worker.js from the site root (for example public/service-worker.js in Vite, Next.js or Create React App) with the importScripts line and the endpoint from the reference.
4. Create one shared push client module and call start() from a user action such as an "Enable notifications" button, never on page load: browsers block permission prompts that are not triggered by a click.
5. If the app has signed-in users, call setUserId with a TokenProvider pointing at the backend token endpoint after sign-in, and stop() on sign-out.
6. Web Push needs HTTPS (localhost is fine for development); on iPhone and iPad it works only once the site is added to the Home Screen.
7. Read the instance ID and endpoint from the project's config or environment, and add placeholder entries to .env.example (or its equivalent).
8. Always set the endpoint above. The Pusher Beams SDKs and their defaults point at pusher.com.
9. If YOUR_INSTANCE_ID above is still a placeholder, keep it as a config placeholder and tell me where to fill it in rather than inventing a value.
10. Subscribe test devices to the "hello" interest so the portal's Send a test reaches them.
11. When you are done, list the files you changed and tell me how to check it works: on the Hibira portal app page, Push notifications › Send a test to the hello interest.

Reference setup from the Hibira push notifications guide (adapt it to the project's structure):

Install:

```shell
npm install @hibira-io/push-web
```

Add the service worker:

```js
// public/service-worker.js, served at https://your-site/service-worker.js
importScripts('https://cdn.jsdelivr.net/npm/@hibira-io/push-web@1/dist/service-worker.js');
self.HibiraPush.endpoint = 'https://ws.hibira.io';
```

Register the browser:

```js
import { Client } from '@hibira-io/push-web';

const push = new Client({
  instanceId: 'YOUR_INSTANCE_ID',
  endpoint: 'https://ws.hibira.io',
});

// Browsers only show the permission prompt after a click or tap.
document.querySelector('#enable-notifications').addEventListener('click', async () => {
  await push.start();
  await push.addDeviceInterest('hello');
});
```

Link the signed-in user:

```js
import { TokenProvider } from '@hibira-io/push-web';

// After sign-in. Your backend answers GET /push/user-token?user_id=... (see the server guide).
await push.setUserId(currentUser.id, new TokenProvider({ url: '/push/user-token' }));

// On sign-out, so the next person on this browser doesn't get their notifications.
await push.stop();
```

3. Publish from your server

Server SDK

Language

Publish from Node.js with the official Beams server SDK, pointed at your endpoint. @pusher/push-notifications-server ↗

  1. Install

    npm install @pusher/push-notifications-server
  2. Publish to an interest

    const PushNotifications = require('@pusher/push-notifications-server');
    
    const beams = new PushNotifications({
      instanceId: 'YOUR_INSTANCE_ID',
      secretKey: process.env.HIBIRA_PUSH_SECRET_KEY,
      endpoint: 'ws.hibira.io',
    });
    beams.protocol = 'https'; // the SDK switches to http when an endpoint is set
    
    await beams.publishToInterests(['hello'], {
      apns: { aps: { alert: { title: 'Hello', body: 'Hello, world!' } } },
      fcm: { notification: { title: 'Hello', body: 'Hello, world!' } },
      web: { notification: { title: 'Hello', body: 'Hello, world!', deep_link: 'https://your-site.example/' } },
    });
  3. Add the user token endpoint

    // Express: the client SDKs call GET /push/user-token?user_id=...
    app.get('/push/user-token', requireLogin, (req, res) => {
      if (req.query.user_id !== String(req.user.id)) return res.sendStatus(401);
      res.json(beams.generateToken(String(req.user.id))); // { token: '...' }
    });
  4. Publish to a signed-in user

    await beams.publishToUsers(['user-1'], {
      apns: { aps: { alert: { title: 'Order shipped', body: 'Order #1024 is on its way.' } } },
      fcm: { notification: { title: 'Order shipped', body: 'Order #1024 is on its way.' } },
      web: { notification: { title: 'Order shipped', body: 'Order #1024 is on its way.' } },
    });
Agent prompt for Node.js

Paste this into Claude Code, Cursor, GitHub Copilot or another AI coding agent. It carries the instance ID and endpoint; the secret key stays an environment variable.

Add Hibira push notifications to this Node.js project. Hibira Push sends to iOS (APNs), Android (FCM) and browsers (Web Push) through a Beams-compatible API, so it works with the official Beams server SDKs pointed at the Hibira endpoint (here: @pusher/push-notifications-server, https://github.com/pusher/push-notifications-node).

Connection details:
- Instance ID: YOUR_INSTANCE_ID
- Endpoint: https://ws.hibira.io
- Secret key: read it from the HIBIRA_PUSH_SECRET_KEY environment variable (server-side only)

What to do:
1. Look at the project first (framework version, package manager, config and env conventions, where similar integrations live) and follow what it already does.
2. Create one shared module that builds the PushNotifications client from environment variables, with the endpoint option and beams.protocol set as in the reference (without them the SDK talks to pusher.com, or to the endpoint over plain http).
3. If the app has signed-in users, add the token route behind the project's existing authentication middleware; it must refuse a user_id that is not the signed-in user's.
4. Publish from the places that should notify (jobs, controllers, event handlers) through the shared module, and log the publish ID it returns.
5. Read the instance ID and endpoint from the project's config or environment, and add placeholder entries to .env.example (or its equivalent). Never commit the secret key or send it to a browser or mobile app.
6. Always set the endpoint above. The Pusher Beams SDKs and their defaults point at pusher.com.
7. If YOUR_INSTANCE_ID above is still a placeholder, keep it as a config placeholder and tell me where to fill it in rather than inventing a value.
8. Subscribe test devices to the "hello" interest so the portal's Send a test reaches them.
9. When you are done, list the files you changed and tell me how to check it works: on the Hibira portal app page, Push notifications › Send a test to the hello interest.

Reference setup from the Hibira push notifications guide (adapt it to the project's structure):

Install:

```shell
npm install @pusher/push-notifications-server
```

Publish to an interest:

```js
const PushNotifications = require('@pusher/push-notifications-server');

const beams = new PushNotifications({
  instanceId: 'YOUR_INSTANCE_ID',
  secretKey: process.env.HIBIRA_PUSH_SECRET_KEY,
  endpoint: 'ws.hibira.io',
});
beams.protocol = 'https'; // the SDK switches to http when an endpoint is set

await beams.publishToInterests(['hello'], {
  apns: { aps: { alert: { title: 'Hello', body: 'Hello, world!' } } },
  fcm: { notification: { title: 'Hello', body: 'Hello, world!' } },
  web: { notification: { title: 'Hello', body: 'Hello, world!', deep_link: 'https://your-site.example/' } },
});
```

Add the user token endpoint:

```js
// Express: the client SDKs call GET /push/user-token?user_id=...
app.get('/push/user-token', requireLogin, (req, res) => {
  if (req.query.user_id !== String(req.user.id)) return res.sendStatus(401);
  res.json(beams.generateToken(String(req.user.id))); // { token: '...' }
});
```

Publish to a signed-in user:

```js
await beams.publishToUsers(['user-1'], {
  apns: { aps: { alert: { title: 'Order shipped', body: 'Order #1024 is on its way.' } } },
  fcm: { notification: { title: 'Order shipped', body: 'Order #1024 is on its way.' } },
  web: { notification: { title: 'Order shipped', body: 'Order #1024 is on its way.' } },
});
```

4. Signed-in users

To reach a person on every device they use, link each device to your own user ID. The client SDK calls your token endpoint with ?user_id=…; the endpoint checks that the request comes from that signed-in user and answers with the token that generateToken makes from your secret key (every Beams server SDK has it). Then publish to the user ID instead of an interest. Deleting a user unlinks all of their devices.

Call stop when the user signs out, so the next person on that device does not get their notifications.

5. Send a test

On the app page, under Push notifications › Send a test, enter an interest (hello in the snippets above), a title and a body, and click Send test. Devices subscribed to that interest get it within seconds, and the Subscribers meter shows how many devices and browsers are registered.

Troubleshooting

A device or browser never shows up under Subscribers
Check that the client calls start with this app’s instance ID and endpoint, and that the user allowed notifications. On Android, google-services.json must come from the same Firebase project as the service account you uploaded.
iPhones and iPads get nothing
The APNs environment must match the build: Development (sandbox) for builds run from Xcode, Production for TestFlight and the App Store. The bundle ID must match the app exactly, and the app target needs the Push Notifications capability.
start() fails with “service worker missing”
Serve /service-worker.js from the root of your site, over HTTPS, with the importScripts line from the Web guide. A service worker in a subfolder only controls that folder.
Safari on iPhone never asks for permission
Web Push on iOS and iPadOS 16.4+ works only after the site is added to the Home Screen, and the permission prompt has to come from a tap.
The server SDK still calls pusher.com, or fails over http
Set the endpoint option. The Node.js SDK also needs beams.protocol = 'https', and the Go SDK takes WithCustomBaseURL.
Publishing answers 401
The server is using the wrong secret key, or one that was rotated longer ago than its grace period. Copy the current key from the app page.
Publishing answers 429
You are publishing faster than your plan allows. Wait for the Retry-After delay, and target up to 100 interests or 1,000 users in one publish instead of one request each.
Registering a device answers 402
The app has reached its plan’s subscriber limit. Upgrade the plan, or delete users who left so their devices are removed.
setUserId fails
Your token endpoint must answer 200 with the JSON that generateToken returns for the user_id it is asked about, using this app’s secret key. A device that is linked to another user must call stop first.

Moving from Pusher Beams? Switch the client SDK packages to ours, set the endpoint in your server SDK, upload your APNs key and FCM service account, and publish to both services until your users have updated. Devices register again on their first start. Migrating from Pusher →