Purpose: Definitive guide to tracking push notification lifecycle events — delivery, open, and action clicks — so Zixflow can measure campaign performance accurately. For step-by-step SDK setup and code samples, see the platform guides in the SDK tab:
- Flutter — Push Notification Tracking
- React Native — Push Notification Tracking
- iOS — Push Notification Tracking
- Android — Push Notification Tracking
- JavaScript — Push Notification Tracking
Why Push Tracking Matters
When Zixflow sends a push notification to a user’s device, it records that the notification was sent. But it cannot know on its own:- Did the notification actually arrive on the device?
- Did the user open it (tap the banner)?
- Did the user tap an action button (“Shop Now”, “Remind Me”)?
The Delivery Lifecycle at a Glance
The Push Payload
Zixflow injects two special fields into every push notification’s data payload. Your app uses these to associate the tracking event with the correct campaign delivery.| Field | Example value | Purpose |
|---|---|---|
Zixflow-Delivery-ID | "626533406292836846" | Unique ID for this delivery (matches campaign record) |
Zixflow-Delivery-Token | "dcFRlDhiRbehM1vg-Lx_yn:..." | The FCM/APNs token the notification was sent to |
message.data (Flutter/Android) or userInfo (iOS native). The SDK’s trackMetric() method requires both to route the event correctly.
Example raw data payload received by the app:
Important:action_buttonsis a JSON string (not a nested object). Parse it withjson.decode()/JSONSerializationbefore use.
The Three Tracking Events
Each interaction maps to a specific SDK call. The event name, parameters, and platform code are listed for each below.1. Delivery Confirmed
Event name (sent to Zixflow):Push Notification DeliveredWhen to fire: The moment the push data payload arrives on the device — inside your
onMessage / onMessageReceived / willPresent handler.SDK method:
Zixflow.instance.trackMetric(deliveryID:, deviceToken:, event: MetricEvent.delivered)What happens: Zixflow updates the campaign delivery record to
delivered. No profile event is stored.
Parameters:
| Parameter | Required | Description |
|---|---|---|
deliveryID | ✅ | Value of Zixflow-Delivery-ID from the push payload |
deviceToken | ✅ | Value of Zixflow-Delivery-Token from the push payload (or your cached FCM/APNs token) |
event | ✅ | MetricEvent.delivered |
2. Notification Opened
Event name (sent to Zixflow):Push Notification OpenedWhen to fire: When the user taps the notification banner. Fire for both body taps and action button taps.
SDK method:
Zixflow.instance.trackMetric(deliveryID:, deviceToken:, event: MetricEvent.opened)What happens: Zixflow updates the campaign delivery record to
opened. No profile event is stored.
Parameters:
| Parameter | Required | Description |
|---|---|---|
deliveryID | ✅ | Value of Zixflow-Delivery-ID from the push payload |
deviceToken | ✅ | Value of Zixflow-Delivery-Token from the push payload |
event | ✅ | MetricEvent.opened |
3. Action Button Clicked
Event name (sent to Zixflow):Push Notification Action ClickedWhen to fire: When the user taps a named action button (“Shop Now”, “Try It Free”, etc.). Always fire
trackMetric(opened) first, then fire this event.SDK method:
Zixflow.instance.track(name: "Push Notification Action Clicked", properties: {...})What happens: Zixflow records a
clicked delivery report and captures which button was tapped for campaign analytics.
This is a namedProperties:track()call — nottrackMetric(). There is noMetricEventenum for clicks.
| Property | Type | Required | Description |
|---|---|---|---|
Zixflow-Delivery-ID | string | ✅ | Links this event to the campaign delivery record |
action_index | integer | ✅ | 0-based index of the button tapped (0 = first button) |
action_name | string | ✅ | Human-readable button label (e.g. "Shop Now") |
Zixflow-Delivery-Token | string | Recommended | Token push was sent to |
action_deeplink | string | Recommended | URL the button navigates to |
title | string | Optional | Notification title |
action_id | string | Optional | OS-level action identifier (e.g. "ACTION_0") |
source | string | Optional | "local_notification" or "web_push" |
Platform-Specific Integration
Flutter (Android + iOS)
Flutter is the reference implementation. The pattern is the same for both platforms once FCM / APNs tokens are obtained.Step 1 — Register Device Token
Step 2 — Track Delivery (Foreground)
When the app is in the foreground, FCM delivers the message toonMessage. Track delivery immediately.
Step 3 — Track Open (Background / Terminated)
Step 4 — Track Action Button Click (Local Notification Response)
For foreground-received notifications displayed as local notifications, handle taps viaflutter_local_notifications:
Android (Kotlin / Java native)
If you are building with native Android (not Flutter), the integration points are the same — only the API surface changes.Token Registration
Track Delivery (Foreground — via onMessageReceived)
Track Open (Notification Tap)
Track Action Button Click
iOS (Swift native)
Token Registration
Track Delivery (Foreground — UNUserNotificationCenterDelegate)
Track Open (Background / Terminated Tap)
Web (JavaScript / TypeScript)
Web push uses the Service Worker to receive notifications while the browser is in the background. The tracking pattern is the same.Token Registration
Track Delivery (Service Worker — push event)
Track Open (Service Worker — notificationclick event)
Action Buttons Format
Theaction_buttons field in the push payload is a JSON-encoded string containing an array of button objects. You must parse it before use.
Payload value (raw string):
- Maximum 2 buttons per notification (iOS system limit; Android supports more but keep it consistent)
deeplinkmay be an empty string — handle gracefully, don’t navigate to a blank URL- Button index is 0-based — button at index 0 is leftmost/first
- On iOS, button labels are pre-registered at app init due to OS constraints. Only the deeplinks are dynamic from the payload. Use generic labels in the pre-registration (“Action 1”, “Action 2”) and rely on the
action_namein the tracking event for analytics.
Deep Link Handling
Every Zixflow push notification may carry adeeplink_url at the top level and per-button deeplinks in action_buttons. Your app is responsible for routing these.
Priority order for navigation on tap:
- If an action button was tapped → use
action_buttons[index].deeplink - If body was tapped → use
deeplink_url - If neither is set → open app to default screen
Decision Flowchart
Use this when deciding which SDK call to make for any notification interaction:Fallback Behaviour (No Zixflow-Delivery-ID)
If the push notification was sent by a non-Zixflow source (e.g., a direct FCM API call, a test tool, or a third-party system),Zixflow-Delivery-ID and Zixflow-Delivery-Token will be absent from the payload.
In this case, skip trackMetric(). Push notification event names (Push Notification Delivered, Push Notification Opened, Push Notification Action Clicked) are reserved for Zixflow’s delivery pipeline — they are not stored as user profile events.
If you want to track a non-Zixflow push for your own analytics (e.g., to build a custom funnel), use a custom event name instead: