Rich Notifications

Send notifications with modern Expo message fields from the native-notify SDK — subtitle, badge, ttl, interruption level, iOS category, Android channel, collapse id, sound, and more.

Native Notify's send helpers pass the modern Expo message fields through to every send path — mass, single subscriber, group, and followers. The same fields are accepted directly by the REST endpoints, and the dashboard and MCP server send them too.

These helpers ship in the next native-notify release — install native-notify@latest to get them (see Upgrading to v5).

Sends are live:

Every helper sends through your Native Notify account the moment it is called — there is no test mode, and a send cannot be recalled. Unset rich fields keep the server's defaults.

Send helpers

HelperSends to
sendMassNotification(title, body, options)Every registered device of the app.
sendIndieNotification(subID, title, message, options)One individual-push subscriber.
sendIndieGroupNotification(subIDs, title, message, options)A list of subscriber ids you already hold.
sendNotificationToFollowers(masterSubID, title, message, options)Every follower of one follow-master subID.

All four take the same options object, resolve with the server's response, and throw when the server rejects the send. sendIndieGroupNotification also throws when subIDs is not a non-empty array — an empty audience would otherwise be a silent no-op.

Quick start

import { sendMassNotification, sendIndieNotification } from 'native-notify';

// Every registered device (mass):
await sendMassNotification('Service update', 'We are back online.', {
  pushData: { url: '/status' },
  subtitle: 'All systems normal',
  channelId: 'alerts',          // Android channel — create it first (below)
  interruptionLevel: 'active',  // iOS
  sound: 'chime.wav',
});

// One subscriber (indie):
await sendIndieNotification(userId, 'Your order', 'It shipped!', {
  pushData: { url: '/orders/' + orderId },
  badge: 1,
  categoryId: 'ORDER_UPDATE',
});

// A custom audience, or the followers of a follow-master:
await sendIndieGroupNotification(subIds, 'Group news', 'Hello all', { channelId: 'news' });
await sendNotificationToFollowers(masterSubId, 'New post', 'Read it', { mutableContent: true });

Options

Every helper accepts the same options object:

OptionTypeWhat it does
pushDataobjectThe data payload your app reads on tap (deep links, ids).
bigPictureURLstringAndroid big-picture image / iOS attachment source.
subtitlestring (max 200 chars)iOS subtitle shown under the title.
badgenumberiOS app-icon badge count — 0 clears the badge.
ttlnumberSeconds the push may be delivered for.
interruptionLevel'passive' | 'active' | 'timeSensitive' | 'critical'iOS interruption level — use timeSensitive / critical sparingly.
categoryIdstringiOS notification category (your action buttons).
channelIdstringAndroid channel id — create the channel first.
collapseIdstringAPNs collapse id — a newer push with the same id replaces the older one.
contentAvailablebooleaniOS background delivery (silent data update).
mutableContentbooleaniOS notification service extension may modify the payload (e.g. attach an image).
soundstring | falseCustom sound file name — or false for a silent notification.
appId / appTokennumber | stringOptional overrides; normally set once with NativeNotify.init() / <NativeNotifyProvider>.

Worth knowing:

  • Only the options you pass are sent. Unset fields keep the server's defaults, and sound: false, badge: 0 and contentAvailable: false are real values, not omissions.
  • The server validates them up front. An invalid value (an unknown interruptionLevel, a non-boolean mutableContent, …) fails the request with a 400 instead of being rejected per recipient by Expo.
  • Raw REST calls accept one more field: expiration (a unix timestamp) as an alternative to ttl. The SDK helpers expose ttl.
  • Open analytics work with these sends. Mass and single-indie sends carry the hidden notification id that Sessions & Push Opens reports on — enable it with analytics: { opens: true }.

Android notification channels: setAndroidNotificationChannel

A channelId in your send has to match a channel the app has created on the device. setAndroidNotificationChannel(channelId, options) is a thin wrapper over expo-notifications' setNotificationChannelAsync():

import * as Notifications from 'expo-notifications';
import { setAndroidNotificationChannel } from 'native-notify';

await setAndroidNotificationChannel('alerts', {
  name: 'Alerts',
  importance: Notifications.AndroidImportance.HIGH,
  sound: 'chime.wav',
  vibrationPattern: [0, 250, 250, 250],
});

// Then send to it:
await sendMassNotification('Heads up', 'The service is back', { channelId: 'alerts' });

Create the channel before sending a push with a matching channelId, so the notification routes to it. The channel's name, sound, importance, and vibration are user-visible in Android's per-app notification settings.

Options — only the keys you set are used:

OptionTypeNotes
namestringHuman-readable name shown in Android settings (defaults to the channel id).
descriptionstringLong description shown in Android settings.
importanceNotifications.AndroidImportanceMAX / HIGH / DEFAULT / LOW / MIN.
soundstring | nullCustom sound file name (omit for the platform default).
vibrationPatternnumber[]Vibration pattern in ms, e.g. [0, 250, 250, 250].
lightColorstringNotification LED color, e.g. '#FF231F7C'.
enableLightsbooleanEnable the notification LED.
enableVibratebooleanEnable vibration for this channel.
showBadgebooleanShow a badge dot on the app icon for this channel's notifications.

setAndroidNotificationChannel() resolves true on Android; it resolves false on iOS/web (Android channels don't exist there) or when the call fails — it never throws, so callers keep their default channel.