Push step settings
Contents
The Push step in the workflow builder sends a notification to every device a person registered with one of your push channels. This page covers each setting on the step and what your app receives.
Text fields accept variables, for example {event.properties.chat_id} or {person.properties.name}. The body uses Liquid instead, for example {{ person.properties.name }}.
Main settings
| Setting | What it does |
|---|---|
| Push channels | The FCM and APNs channels to send through. Each person gets the notification on the devices they registered with those channels. People with no device on a channel are skipped and aren't counted as a send. |
| Delivery | Whether the device shows the notification, or only your app gets it. See Delivery. |
| Notification title | Required unless the push is silent. |
| Notification body | Optional text under the title. |
| Image URL | Optional image shown in the notification. On iOS, your app needs a notification service extension to display it. |
| Link | Where a tap should take the person. See Link and group. |
| Group | Bundles related notifications together, such as one conversation. See Link and group. |
| Custom data payload | Key-value pairs delivered to your app. Values must be strings, because FCM only accepts strings. |
| Collapse key | On iOS, a notification with the same key replaces the one already shown. On Android, it only collapses messages still waiting for an offline device. To replace a notification already shown on Android, use Android tag. |
| TTL (seconds) | How long the provider keeps the message for a device that's offline. |
The step also has Android and iOS sections for platform settings: priority, channel ID, sound, tag, icon, color and click action on Android, and sound, badge, subtitle, category, thread ID, interruption level and mutable content on iOS.
Delivery
| Option | Android | iOS |
|---|---|---|
| Show a notification (default) | The system shows the notification. | The system shows the notification. |
| Data only on Android, notification on iOS | Nothing is shown. Your app gets the message in onMessageReceived, even in the background, and draws the notification itself. | The system shows the notification. |
| Silent, show nothing | Nothing is shown. Your app gets the message in onMessageReceived. | Nothing is shown. iOS wakes your app in the background with the data. |
With Show a notification, the system only shows the notification while your app is in the background. When your app is in the foreground:
- On Android, the message goes to
onMessageReceived, and your app has to show it. - On iOS, your
UNUserNotificationCenterDelegatehas to return the presentation options, such as[.banner, .sound], fromuserNotificationCenter(_:willPresent:withCompletionHandler:).
For data only and silent pushes, put everything your app needs in Custom data payload. On Android, the title, body and image aren't sent with a data only push, and Android priority is the only Android setting that applies. A silent push sends no title, body or image to any device.
Things to know about silent pushes on iOS:
- Your app needs the Remote notifications background mode, and it receives the push in
application(_:didReceiveRemoteNotification:fetchCompletionHandler:). - Apple sends them at low priority and limits how often an app is woken, so a silent push can arrive late or not at all. Don't use them for anything time critical.
If you create a workflow through the API and leave out the title for a push that shows a notification, PostHog uses the template's default title.
Link and group
Link and Group are sent to your app in the custom data, as the link and group keys. If you also set a link key in Custom data payload, the Link field replaces it.
On iOS, Group is also used as the thread ID, so iOS bundles notifications with the same group. If you set iOS thread ID, that's used instead.
PostHog SDKs don't open the link, and they don't group notifications on Android, so your app has to read these keys itself.
On Android, read them where your app handles the push. For a data only or silent push, that's onMessageReceived, where you can also pass group to setGroup() when you build the notification:
When the system shows the notification and the app is in the background, a tap opens your launcher activity with the data in its intent extras:
On iOS, read them when the person taps the notification:
Protect sensitive data
Turn on Protect sensitive data in the step's settings panel for notifications that carry private content, such as direct messages or sign-in codes. It's off by default.
When it's on:
- The notification is still sent as usual.
- The workflow doesn't keep a copy of the title, subtitle, body or image as sent. The workflow's Assets tab and the person's Push notifications tab show that a notification was sent, with the title as you wrote it in the step, before variables are filled in, and a note that the content wasn't stored.
- The workflow run doesn't keep its variables, so the run can't be rerun.
These tabs never include a push's custom data, whether or not this setting is on.
This setting only covers what the workflow stores. When someone taps the notification, the iOS SDK captures a $push_notification_opened event with the notification's title, subtitle and body. To keep that content out of your events, remove it before the event is sent:
On Android, opens captured automatically carry no title or body. React Native and Flutter apps capture iOS opens in the native SDK, where your JavaScript or Dart before send hook can't reach them. In those apps, turn off capturePushNotificationOpened and capture opens yourself without the content. Whenever you capture an open yourself, on any platform, don't pass the content.





