LerixBlog
ArticlesChangelogChangelogDocs
العربية
Dashboard
Lerix

The Lerix blog: guides for app developers, and news of every release of Lerix and its SDKs.

Blog

ArticlesChangelog RSS feed

Lerix

HomePricingDocsRefund policy

© 2026 Lerix · All rights reserved

LerixBlog
ArticlesChangelogChangelogDocs
العربية
Dashboard
  1. Lerix Blog
pushflutterweb

Send a push notification to a person, not a device

Your users have a phone, a laptop and a browser tab. Stop storing device tokens: link devices to your own user ID and send once to iOS, Android, web and macOS.

The Lerix team

30 October 20266 min read

One user in the centre, receiving the same notification on an iPhone, an Android phone, Chrome and a Mac
On this page
  1. 1. Set up push on each platform once
  2. 2. Tell Lerix who is signed in

Read next

A Flutter checkout screen failing, its stack trace, and the grouped issue in the Lerix dashboard
flutterع

Crash reporting for Flutter apps: a practical setup guide

Most users never report a crash, they just leave. Set up crash reporting in a Flutter app in minutes: what gets caught, how to add context, and how to cut the noise.

30 October 20266 min read

An 'Update required' screen, the release control settings, and a 'Back soon' maintenance screen
release controlع

Force an app update or turn on maintenance mode, without shipping a release

Lerix

The Lerix blog: guides for app developers, and news of every release of Lerix and its SDKs.

Blog

ArticlesChangelog RSS feed

Lerix

HomePricingDocsRefund policy

© 2026 Lerix · All rights reserved

  • 3. Send once, from your server
  • 4. Don't let anyone claim someone else's id
  • 5. See what was delivered, per device
  • Checklist
  • Your backend knows a user as u_8241. Their push notifications, though, go to devices: an iPhone, an Android tablet, a Chrome tab on a laptop, maybe the Mac app. The usual way to bridge that is a device_tokens table: save each token when the app registers it, keep it in sync on sign-out, delete it when a provider says it's dead, and loop over the list on every send.

    Lerix takes that table off your hands. Each app tells Lerix who is signed in, and your server sends to your own user id. Lerix finds every device that person uses, on every platform, and reports delivery per device.

    Sign-in calls setUser on each device, Lerix links the devices to the user, and one send with externalUserIds reaches all of them

    1. Set up push on each platform once

    Each platform needs its credentials in the dashboard (Notifications → Settings) and a few lines in the app:

    Platform In the dashboard In the app
    iOS and macOS upload your APNs .p8 key (one key covers both) Push Notifications capability, Background Modes → Remote notifications
    Android upload your Firebase service account JSON nothing to add: no google-services.json, no Google Services plugin
    Web Web Push → Generate keys copy the lerix-sw.js service worker to your site

    On Android the SDK fetches your project's sender id from Lerix at runtime, which is why the app doesn't carry a Firebase config file.

    In Flutter, push lives in its own package next to the core one:

    dependencies:
      lerix_flutter: ^0.7.0
      lerix_messaging: ^0.7.0
    
    await Lerix.notifications.init();
    await Lerix.notifications.requestPermissions();
    

    On the web, with @lerix-dev/lerix-js:

    import { Lerix } from '@lerix-dev/lerix-js';
    
    Lerix.init({ apiKey: 'your-api-key', projectId: 'your-project-slug' });
    await Lerix.notifications.init();
    await Lerix.notifications.subscribe(); // asks for permission, registers the service worker
    

    Safari supports Web Push from macOS 13 and iOS 16.4, for sites added to the Home Screen on iOS.

    2. Tell Lerix who is signed in

    Call setUser with your own user id right after sign-in, on every platform, and clearUser on sign-out:

    // Flutter
    await Lerix.setUser(user.id);
    // …
    await Lerix.clearUser();
    
    // Web
    await Lerix.setUser(user.id);
    // …
    await Lerix.clearUser();
    

    The native SDKs have the same pair: Lerix.setUser(_:) on iOS and Lerix.setUser(...) on Android.

    A few things that make this safe to call anywhere:

    • It's fine to call before the SDK has finished starting up; the id is saved and sent once registration completes.
    • Devices are linked to the user, never merged. Each install keeps its own record, so a phone and a laptop stay two devices of one person.
    • Always call clearUser() on sign-out. Otherwise a shared device keeps receiving the previous person's notifications.

    Use a stable id from your own system. Avoid emails and phone numbers: they change, and they're personal data you don't need to share.

    3. Send once, from your server

    Sending goes through the public API with a project private key that has the send push notifications permission (Project settings → API keys):

    curl -X POST https://api.lerix.dev/v1/your-project-slug/notifications/send \
      -H "lerix-key: <your-private-key>" \
      -H "Content-Type: application/json" \
      -d '{
        "title": "Your order shipped",
        "body": "It arrives Thursday.",
        "externalUserIds": ["u_8241", "u_77"],
        "metadata": { "orderId": "A-1042" }
      }'
    

    The answer tells you how far it got:

    {
      "success": true,
      "notificationId": "b6b1a2b0-…",
      "matchedUsers": 1,
      "matchedDevices": 3,
      "unknownUserIds": ["u_77"]
    }
    

    unknownUserIds lists ids that have no device yet, a person who signed up on the web and never allowed notifications for example. That isn't an error.

    From a NestJS backend, the server SDK does the same in one call:

    const { matchedDevices, unknownUserIds } = await this.lerix.notifications.send({
      title: 'New message',
      body: 'You have a reply',
      externalUserIds: [user.id],
    });
    

    The Python (lerix) and .NET (Lerix) server SDKs have the same option (external_user_ids, ExternalUserIds).

    Rules worth knowing:

    • Up to 1,000 user ids per send.
    • One target per send: externalUserIds, deviceTokens, or a topic. Mixing them is rejected.
    • imageUrl adds a picture: automatic on Android and the web; on iOS it needs a Notification Service Extension.
    • metadata arrives in the app with the notification, for deep links and the like.
    • sentAt schedules the send. Devices are looked up again at send time, so someone who signs in on a new phone after you schedule still gets it.

    You can do the same from the dashboard: Send notification → Users, paste the ids, and it shows how many devices will receive it before you send.

    4. Don't let anyone claim someone else's id

    setUser runs in the app, so without a check, a modified client could call setUser('u_8241') and start receiving that person's notifications. Identity verification closes that gap: your server signs the user id, and Lerix only accepts ids with a valid signature.

    1. In Notifications → Settings → Identity verification, click Generate secret and store it on your server, for example as LERIX_IDENTITY_SECRET. It's shown once.
    2. When a user signs in, compute the lowercase hex HMAC-SHA256 of their id, keyed with that secret, and return it to the app:
    import { createHmac } from 'node:crypto';
    
    const lerixHash = createHmac('sha256', process.env.LERIX_IDENTITY_SECRET!)
      .update(user.id, 'utf8')
      .digest('hex');
    

    The NestJS SDK has this as identityHash(secret, user.id), and the Python one as identity_hash.

    1. Pass it with the id:
    await Lerix.setUser(user.id, identityHash: session.lerixHash);
    
    await Lerix.setUser(user.id, { identityHash: session.lerixHash });
    
    1. Once every app version sends the hash, turn on Require verified identity. From then on, setUser without a valid hash is refused.

    Never put the secret in an app. Rotate secret replaces it, and hashes made with the old one stop working.

    5. See what was delivered, per device

    Every send records one delivery per device with its platform (ios, android, web, macos, windows), its status and the reason when it failed:

    curl https://api.lerix.dev/v1/your-project-slug/notifications/<notificationId>/deliveries \
      -H "lerix-key: <your-private-key>"
    

    When one platform isn't configured yet (no Apple key uploaded, say), its devices are recorded as PLATFORM_NOT_CONFIGURED and the other devices in the same send are delivered normally. Tokens a provider reports as invalid are removed for you.

    Sent something by mistake? Unsend removes it from iOS, Android and macOS devices (with an SDK version that supports it) and keeps it in your history as unsent. Web and Windows notifications can't be pulled back once shown.

    Checklist

    1. Credentials uploaded for each platform you ship on.
    2. Push set up in each app (lerix_messaging in Flutter, the service worker on the web).
    3. setUser(yourUserId) after sign-in, clearUser() on sign-out, on every platform.
    4. Sends from your server with externalUserIds and a private key.
    5. Identity verification on before you rely on it for anything personal.

    Then sign in with the same account on two devices and send yourself one notification. Seeing it arrive on both is the whole point.

    pushflutterweb
    Share X LinkedIn

    On this page

    1. 1. Set up push on each platform once
    2. 2. Tell Lerix who is signed in
    3. 3. Send once, from your server
    4. 4. Don't let anyone claim someone else's id
    5. 5. See what was delivered, per device
    6. Checklist

    An old version breaks against your new API, or the backend needs an hour offline. Set update rules and maintenance windows from the dashboard and every app follows within minutes.

    30 October 20267 min read

    A minified stack trace next to the same error mapped back to TypeScript source
    source mapsع

    Readable stack traces from minified JavaScript: source maps and error grouping

    index-8f3a1c.js:1:48213 tells you nothing. Upload source maps from your build so every error points at your own file, line and function, and see how errors become issues.

    30 October 20266 min read

    Catch your next crash before your users report it

    Crash reporting, push, analytics and an AI assistant that reads your code. Free to start.

    Start free