Push notification
Initialize the SDK
When using JavaScript-controlled initialization, call initialize() before registering listeners or requesting push notifications:
import Reteno from 'expo-reteno-sdk';
await Reteno.initialize('YOUR_SDK_ACCESS_KEY');Skip this call if sdkAccessToken is set in the platform plugin config, because that enables automatic initialization.
Register for push notifications
Call registerForRemoteNotifications() once at app startup:
import { useEffect } from 'react';
import Reteno from 'expo-reteno-sdk';
useEffect(() => {
Reteno.registerForRemoteNotifications();
}, []);Get initial notification
When your app is launched by clicking a Reteno push notification, including an iOS cold start from a terminated state, read the launch payload with getInitialNotification().
The method returns the Reteno payload or null. On Android, launch intent extras from other integrations, such as Branch links, are ignored and do not appear as an initial Reteno notification. Call the method once during app startup; on iOS the stored cold-start response is consumed by the first call.
import { useEffect } from 'react';
import { Alert } from 'react-native';
import Reteno from 'expo-reteno-sdk';
useEffect(() => {
Reteno.getInitialNotification().then((data) => {
if (data) {
Alert.alert('getInitialNotification', JSON.stringify(data));
}
});
}, []);Use setOnRetenoPushClickedListener below for notification clicks received after the app has started.
Listen for new push notifications while app is active
To listen to pushes in foreground, use setOnRetenoPushReceivedListener:
import { useEffect } from 'react';
import { Alert } from 'react-native';
import Reteno from 'expo-reteno-sdk';
useEffect(() => {
const pushListener = Reteno.setOnRetenoPushReceivedListener((event) => {
Alert.alert('onRetenoPushReceived', event ? JSON.stringify(event) : 'null');
});
return () => pushListener.remove();
}, []);Listen for Push Notification Clicks
To handle notification clicks, use setOnRetenoPushClickedListener:
import { useEffect } from 'react';
import { Alert } from 'react-native';
import Reteno from 'expo-reteno-sdk';
useEffect(() => {
const pushClickListener = Reteno.setOnRetenoPushClickedListener((event) => {
Alert.alert('onRetenoPushClicked', event ? JSON.stringify(event) : 'null');
});
return () => pushClickListener.remove();
}, []);iOS Action Buttons
For iOS push action buttons, use setOnRetenoPushButtonClickedListener:
import { useEffect } from 'react';
import { Platform } from 'react-native';
import Reteno from 'expo-reteno-sdk';
useEffect(() => {
if (Platform.OS !== 'ios') return;
const listener = Reteno.setOnRetenoPushButtonClickedListener((event) => {
console.log('onRetenoPushButtonClicked', event);
});
return () => listener.remove();
}, []);Set device token manually
Use setDeviceToken() when managing the iOS FCM/APNs token manually:
const result = await Reteno.setDeviceToken(token);The method returns Promise<boolean>. On Android it resolves successfully without changing the token because token handling is performed by the native Firebase messaging service.
Listen for Push Notification Dismissed (Android only)
To handle when a user dismisses a push notification, use setOnRetenoPushDismissedListener:
import { useEffect } from 'react';
import { Platform } from 'react-native';
import Reteno from 'expo-reteno-sdk';
useEffect(() => {
if (Platform.OS !== 'android') return;
const listener = Reteno.setOnRetenoPushDismissedListener((event) => {
console.log('onRetenoPushDismissed', event);
});
return () => listener?.remove();
}, []);Listen for Custom / Silent Push Data (Android only)
To receive custom or silent push payloads, use setOnRetenoCustomPushDataListener:
import { useEffect } from 'react';
import { Platform } from 'react-native';
import Reteno from 'expo-reteno-sdk';
useEffect(() => {
if (Platform.OS !== 'android') return;
const listener = Reteno.setOnRetenoCustomPushDataListener((event) => {
console.log('onRetenoCustomPushReceived', event);
});
return () => listener?.remove();
}, []);Notification Permission (Android only)
Request notification permission at runtime and check its status:
import { Platform } from 'react-native';
import Reteno from 'expo-reteno-sdk';
// Request permission
if (Platform.OS === 'android') {
const isGranted = await Reteno.requestNotificationPermission();
console.log('Permission granted:', isGranted);
}
// Check current status
if (Platform.OS === 'android') {
const status = await Reteno.getNotificationPermissionStatus();
// status: 'ALLOWED' | 'DENIED' | 'PERMANENTLY_DENIED' | null
console.log('Permission status:', status);
}Group notifications (Android only)
Requires Reteno Android SDK 2.10.0 or newer (bundled since
expo-reteno-sdkv2.2.0).
Notifications can be grouped by a value in the push payload or by a constant group ID. The rule is persisted natively and restored before JavaScript starts, so it also applies to notifications received while the app is not running.
import Reteno from 'expo-reteno-sdk';
// Group by a payload value, e.g. all pushes for the same chat
await Reteno.setNotificationGroupingRule({ payloadKey: 'chatId' });
// Group under a constant ID, regardless of payload
await Reteno.setNotificationGroupingRule({ groupId: 'messages' });
// Also show the collapsed "N new notifications" summary row Android displays
// when it stacks the group
await Reteno.setNotificationGroupingRule({ groupId: 'messages', showSummary: true });
// Disable grouping
await Reteno.setNotificationGroupingRule(null);The rule must contain exactly one non-empty payloadKey or groupId.
Summary notification (showSummary)
showSummary)Requires
expo-reteno-sdkv2.3.0or newer.
Pass showSummary: true to also get the collapsed "N new notifications" row Android shows when it stacks a group. The SDK creates and maintains this summary notification for you natively ā no native code or manual NotificationCompat setup needed. It:
- creates a dedicated
reteno_group_summarynotification channel (Android 8.0+), - posts/updates the summary once at least two notifications share a group, using the fixed text "New notifications" / "You have N new notifications" (not currently customizable),
- removes the summary once fewer than two grouped notifications remain, e.g. after the user dismisses one down to a single leftover,
- automatically clears any existing summary if you call
setNotificationGroupingRuleagain with a differentpayloadKey/groupId.
Requires Android 6.0 (API 23) or higher ā on older devices the call still resolves successfully and grouping still applies, but no summary is shown. Like any notification, posting the summary also requires the POST_NOTIFICATIONS runtime permission on Android 13+ (see Notification Permission (Android only) above); if it isn't granted, the summary is silently skipped until permission is granted and the next push arrives.
Upgrading from a manual summary implementation? Earlier versions of this guide showed how to
post the summary yourself fromMainApplication.ktusing
RetenoNotificationGroupingRuleProvider.resolveGroup(...). That helper is no longer part of the
public API inv2.3.0ā delete that native code and passshowSummary: trueinstead.
Auto-open links behavior
Use these methods to control whether SDK opens links from push/in-app automatically:
import Reteno from 'expo-reteno-sdk';
await Reteno.setAutoOpenLinks(true); // enable
const isEnabled = await Reteno.getAutoOpenLinks();
console.log('Auto-open links:', isEnabled);Default value:
- iOS:
true - Android:
false
Important for Expo
expo-reteno-sdkrequires a development build or bare app.- Expo Go is not supported for push features using native module integration.