Send subtitle, badge, ttl, sound, channel and other Expo message fields with the native-notify send helpers.
Sends are live
Every helper sends the moment you call it. There's no test mode, and a send can't be recalled.
Send helpers
Needs native-notify v5.2 or later (npm install native-notify@^5.2.0). See Upgrading to v5.
| Helper | Sends 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. They pass Expo's message fields through to the messages Native Notify delivers through Expo, and the REST endpoints and Native Notify's send form accept the same fields.
Send a rich notification
import {
sendMassNotification,
sendIndieNotification,
sendIndieGroupNotification,
sendNotificationToFollowers,
} 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 });
Each helper resolves with the server's response body:
| Result | When |
|---|---|
Resolves with "Success!" | The push went out. |
Resolves with an explanation (status 201) | A lapsed trial, a Premium plan on group and follower sends, an unregistered indie subID, or a follow-master with no followers. Check the body: resolving isn't delivery. |
| Throws on an error status | 400 for a missing title or body, an invalid rich field, or a message over Expo's 4 KiB limit (notification payload exceeds Expo's 4KiB limit). |
| Throws on timeout | A helper gives up after 10 seconds while the server keeps sending, so don't retry blindly. |
| Throws before sending | sendIndieGroupNotification when subIDs isn't a non-empty array. |
Options
| Option | Type | Notes |
|---|---|---|
pushData | object | The data your app reads on tap (deep links, ids). |
bigPictureURL | string | Image URL. Android devices that Native Notify delivers to directly through FCM v1 show it. Expo-delivered messages don't carry it. |
subtitle | string (max 200 chars) | iOS subtitle under the title. |
badge | number (non-negative integer) | iOS app icon badge count. 0 clears it. |
ttl | number (> 0) | Seconds the push may be delivered for. |
interruptionLevel | 'passive' | 'active' | 'timeSensitive' | 'critical' | iOS interruption level. Use timeSensitive and critical sparingly. |
categoryId | string | iOS notification category (your action buttons). |
channelId | string | Android channel id. Create the channel first. |
collapseId | string | APNs collapse id. A newer push with the same id replaces the older one. |
contentAvailable | boolean | iOS background delivery (silent data update). |
mutableContent | boolean | Lets an iOS notification service extension modify the payload, for example to attach an image. |
sound | string | false | Custom sound file name, or false for a silent notification. |
appId / appToken | number | string | Optional overrides. Normally set once with NativeNotify.init(). The send helpers don't read <NativeNotifyProvider>. |
- Only the options you pass are sent, and the server validates them up front: an invalid value (an unknown
interruptionLevel, a non-booleanmutableContent) fails with400, not per recipient at Expo.sound: false,badge: 0andcontentAvailable: falseare real values, not omissions. - Raw REST calls also accept
expiration, a unix timestamp, as an alternative tottl. - The fields apply however a device is reached. If an app saved its own FCM v1 service account or APNs key in Native Notify, its devices are delivered directly through FCM and APNs and the fields travel in those payloads: sound, badge, subtitle,
ttland the rest on iOS, high priority and the data fields on Android. - Mass and single-indie sends carry the hidden notification id that Sessions & push opens reports on. Enable it with
analytics: { opens: true }, with your ids set inNativeNotify.init()or passed toregisterNNPushToken.
Android notification channels
A channelId has to match a channel the app has created on the device. setAndroidNotificationChannel(channelId, options) wraps expo-notifications' setNotificationChannelAsync(), so create the channel before you send to it:
import * as Notifications from 'expo-notifications';
import { setAndroidNotificationChannel, sendMassNotification } 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' });
Only the keys you set are used:
| Option | Type | Notes |
|---|---|---|
name | string | Name shown in Android settings. Defaults to the channel id. |
description | string | Description shown in Android settings. |
importance | Notifications.AndroidImportance | MAX, HIGH, DEFAULT, LOW or MIN. |
sound | string | null | Custom sound file name. Omit for the platform default. |
vibrationPattern | number[] | Vibration pattern in ms, like [0, 250, 250, 250]. |
lightColor | string | Notification LED color, like '#FF231F7C'. |
enableLights | boolean | Enable the notification LED. |
enableVibrate | boolean | Enable vibration for this channel. |
showBadge | boolean | Show a badge dot on the app icon for this channel's notifications. |
It resolves true on Android. On iOS and the web, or when the call fails, it resolves false and never throws, so callers keep their default channel.