diff --git a/v7/api-reference/push-notifications/send.mdx b/v7/api-reference/push-notifications/send.mdx index e2ee017..16d6b05 100644 --- a/v7/api-reference/push-notifications/send.mdx +++ b/v7/api-reference/push-notifications/send.mdx @@ -28,6 +28,54 @@ Requires the `push` bundle. Optional key-value data payload forwarded to the app alongside the notification. Values must be strings or serializable primitives. Use this to pass deep-link targets, IDs, or any context your app needs when the user taps the notification. +### Rich payload fields + +All optional. Each is mapped to whichever platform(s) support it at dispatch time and silently ignored on platforms that have no equivalent. See the [Push Notifications guide](/push-notifications#rich-notification-payloads) for the client-side setup some of these require (Android channels, the iOS Notification Service Extension, your web service worker). + + + Sound file to play. iOS: APNs `sound`. Android: governed by the channel on 8+ — pair with `channelId`. Web: forwarded to the service worker. + + + + iOS app-icon badge count (APNs `badge`). No Android equivalent. The caller supplies the number; Sublay tracks no unread state. + + + + Android notification channel id (FCM `android.notification.channel_id`). The channel must be created client-side; on Android 8+ it owns sound/importance/vibration. + + + + `"high"` or `"normal"`. iOS: APNs `apns-priority` (10/5). Android: FCM `android.priority`. `high` wakes the device for time-sensitive pushes. + + + + iOS subtitle line under the title (APNs `alert.subtitle`). + + + + Rich/big-picture image URL. Android (FCM `image`) and Web work out of the box. iOS additionally requires a Notification Service Extension in the app — the URL is forwarded in the payload and `mutable-content` is set automatically. + + + + Display-replace key so notifications collapse in the UI (FCM `android.notification.tag`, Web `tag`). + + + + Transport-level collapse identifier (APNs `apns-collapse-id`, FCM `collapse_key`) — supersedes an undelivered push with the same id. + + + + iOS notification grouping (APNs `thread-id`). + + + + Time-to-live in seconds for offline devices (APNs `apns-expiration` computed from now, FCM `android.ttl`). + + + + iOS `mutable-content` flag, enabling the app's Notification Service Extension to modify the payload (e.g. attach `imageUrl`). Set implicitly when `imageUrl` is provided. + + ## Response Returns `200` with a `results` map keyed by user ID. Every requested `userId` is present in the response — users with no registered devices get an empty array rather than being omitted. diff --git a/v7/node-sdk/push-notifications.mdx b/v7/node-sdk/push-notifications.mdx index b772722..f6be0f8 100644 --- a/v7/node-sdk/push-notifications.mdx +++ b/v7/node-sdk/push-notifications.mdx @@ -21,6 +21,10 @@ const result = await sublay.push.send({ title: "New message", body: "You have a new message from Alice.", data: { conversationId: "conv_xyz789" }, + sound: "notification.wav", + channelId: "messages", + badge: 3, + tag: "conv_xyz789", }); ``` @@ -40,6 +44,54 @@ const result = await sublay.push.send({ Optional key-value payload forwarded to the app alongside the notification. Use for deep links, entity IDs, or any context your app needs when the user taps. +#### Rich payload fields + +All optional. Each maps to whichever platform(s) support it and is silently ignored elsewhere (e.g. `subtitle`/`threadId` are iOS-only, `channelId` is Android-only). See [Rich notification payloads](/push-notifications#rich-notification-payloads) for the platform setup each one needs. + + + Sound file to play. **iOS:** APNs `sound`. **Android:** on 8+ the sound is owned by the notification channel — pair this with `channelId` and create that channel client-side with the sound set. **Web:** forwarded to your service worker. + + + + iOS app-icon badge count (APNs `badge`). No Android equivalent. Your backend supplies the number — Sublay does not track per-user unread counts. + + + + Android notification channel id (FCM). The channel must be created client-side; on Android 8+ it governs sound, importance, and vibration. + + + + Delivery priority. `high` wakes the device for time-sensitive pushes; `normal` is power-considerate. Maps to APNs `apns-priority` and FCM `android.priority`. + + + + iOS subtitle line shown under the title (APNs `alert.subtitle`). + + + + Rich/big-picture image URL. Works out of the box on **Android** (FCM) and **Web**. On **iOS** it additionally requires a [Notification Service Extension](https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension) in your app; the URL is forwarded in the payload and `mutable-content` is set automatically. + + + + Display-replace key so notifications collapse in the UI instead of stacking (FCM `android.notification.tag`, Web `tag`). Use a per-conversation value to keep one entry per thread. + + + + Transport-level collapse identifier (APNs `apns-collapse-id`, FCM `collapse_key`) — a newer push supersedes an undelivered one with the same id. + + + + iOS notification grouping (APNs `thread-id`). + + + + Time-to-live in **seconds** for offline devices (APNs `apns-expiration`, FCM `android.ttl`). After this window the provider stops retrying. + + + + iOS `mutable-content` flag, letting your Notification Service Extension modify the payload (e.g. attach `imageUrl`). Set implicitly when `imageUrl` is provided. + + **Returns** — `Promise` ```typescript diff --git a/v7/push-notifications.mdx b/v7/push-notifications.mdx index 1e8bbd2..e43be25 100644 --- a/v7/push-notifications.mdx +++ b/v7/push-notifications.mdx @@ -115,6 +115,66 @@ A single call fans the notification out to all platforms. Capped at **100 user I A common pattern is to bridge Sublay's in-app notifications to push: subscribe to the `notification.created` webhook and call `push.send()` when it fires. See [Webhooks → Push Notification Bridge](/webhooks#push-notification-bridge). +## Rich Notification Payloads + +Beyond `title`, `body`, and `data`, `push.send()` accepts optional fields for sound, badges, images, grouping, priority, and lifetime. Each maps to the native APNs / FCM / Web Push capability and is **silently ignored on platforms that don't support it** — so you can set `subtitle` (iOS-only) and `channelId` (Android-only) in the same call without branching. + +```typescript +await sublay.push.send({ + userIds: ["usr_abc123"], + title: "New message", + body: "Alice sent you a message", + data: { conversationId: "conv_xyz789" }, + sound: "notification.wav", // custom sound + channelId: "messages", // Android channel (see below) + badge: 3, // iOS app-icon badge + tag: "conv_xyz789", // collapse a conversation into one notification + priority: "high", +}); +``` + +| Field | Platforms | Notes | +|-------|-----------|-------| +| `sound` | iOS, Android, Web | Filename of a bundled sound. On Android 8+ the **channel** owns the sound — see below. | +| `badge` | iOS | App-icon badge count. Your backend supplies the number; Sublay tracks no unread state. | +| `channelId` | Android | Notification channel id. Created client-side. | +| `priority` | iOS, Android | `"high"` (default) wakes the device; `"normal"` is power-considerate. | +| `subtitle` | iOS | Line under the title. | +| `imageUrl` | iOS, Android, Web | Big-picture image. iOS needs a Notification Service Extension — see below. | +| `tag` | Android, Web | Display-replace key so notifications collapse instead of stacking. | +| `collapseId` | iOS, Android | Transport-level collapse — a newer push supersedes an undelivered one. | +| `threadId` | iOS | Notification grouping. | +| `ttl` | iOS, Android | Time-to-live in **seconds** for offline devices. | +| `mutableContent` | iOS | Enables your Notification Service Extension. Set automatically when `imageUrl` is present. | + +### Client-side requirements + +Three of these need setup in **your app** — Sublay forwards the field but cannot do this part for you: + + + + On Android 8+ the **notification channel** owns the sound, importance, and vibration — the payload can't override it. Create the channel once in your app (with the bundled sound) and pass its id as `channelId`: + + ```ts + import * as Notifications from "expo-notifications"; + + await Notifications.setNotificationChannelAsync("messages", { + name: "Messages", + importance: Notifications.AndroidImportance.HIGH, + sound: "notification.wav", // bundled in the app + }); + ``` + + Then `push.send({ ..., channelId: "messages", sound: "notification.wav" })`. The `sound` field alone is only a pre-Android-8 fallback. Sublay does **not** create channels for you. + + + iOS does not download remote images from the payload on its own. To show `imageUrl` on iOS, add a [Notification Service Extension](https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension) to your app that reads the URL from the notification payload, downloads it, and attaches it. Sublay sets `mutable-content` automatically whenever `imageUrl` is present so the extension is allowed to run. Android and Web render `imageUrl` with no extra work. + + + Sublay ships no service worker — your app's own SW renders web notifications. The server forwards the web-renderable fields (`sound`, `image`, `tag`) in the push JSON; your `push` event handler decides how to display them via `registration.showNotification(...)`. + + + ## Device Lifecycle - **Re-registration:** registering the same physical device again (same token or endpoint) updates the existing record instead of duplicating it.