> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zixflow.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Push Notification Tracking

> Push Notification Tracking — Zixflow React Native SDK integration guide.

### Overview

Zixflow automatically tracks push notification delivery and engagement metrics to help you measure campaign performance. The SDK tracks three key lifecycle events:

1. **Delivery Confirmed** - When the notification arrives on the device
2. **Notification Opened** - When the user taps the notification
3. **Action Button Clicked** - When the user taps an action button

### How Push Tracking Works

When Zixflow sends a push notification, it includes special tracking fields in the data payload:

| Field                    | Purpose                                                |
| ------------------------ | ------------------------------------------------------ |
| `Zixflow-Delivery-ID`    | Unique ID for this delivery (links to campaign record) |
| `Zixflow-Delivery-Token` | The FCM/APNs token the notification was sent to        |

**Example push payload:**

```json theme={null}
{
  "Zixflow-Delivery-ID": "626533406292836846",
  "Zixflow-Delivery-Token": "dcFRlDhiRbehM1vg...",
  "title": "Flash Sale! 70% OFF",
  "body": "Limited time only — grab your deal now!",
  "deeplink_url": "https://yourapp.com/sale",
  "image_url": "https://cdn.yourapp.com/banner.png",
  "action_buttons": "[{\"name\":\"Shop Now\",\"deeplink\":\"https://yourapp.com/sale\"},{\"name\":\"Remind Me\",\"deeplink\":\"\"}]"
}
```

> **Note:** The SDK automatically handles most tracking for you when using the default push implementation. This section covers advanced customization scenarios.

***

### Tracking Events

#### 1. Delivery Confirmed

**When to track:** The moment the push payload arrives on the device

**SDK method:** `Zixflow.trackMetric({ deliveryID, deviceToken, event: MetricEvent.delivered })`

**Implementation:**

```typescript theme={null}
import messaging from '@react-native-firebase/messaging';
import { Zixflow, MetricEvent } from 'zixflow-reactnative';

// Listen for foreground messages
useEffect(() => {
  const unsubscribe = messaging().onMessage(async (remoteMessage) => {
    const deliveryId = remoteMessage.data?.['Zixflow-Delivery-ID'] ?? '';
    const deliveryToken = remoteMessage.data?.['Zixflow-Delivery-Token'] ?? cachedToken ?? '';

    if (deliveryId && deliveryToken) {
      await Zixflow.trackMetric({
        deliveryID: deliveryId,
        deviceToken: deliveryToken,
        event: MetricEvent.delivered,
      });
    }
  });

  return unsubscribe;
}, []);
```

> **Important:** The Zixflow SDK automatically tracks delivery. Manual tracking is only needed for custom implementations.

***

#### 2. Notification Opened

**When to track:** When the user taps the notification banner

**SDK method:** `Zixflow.trackMetric({ deliveryID, deviceToken, event: MetricEvent.opened })`

**Implementation:**

```typescript theme={null}
import messaging from '@react-native-firebase/messaging';
import { Zixflow, MetricEvent } from 'zixflow-reactnative';

// Background / quit tap
useEffect(() => {
  const unsubscribe = messaging().onNotificationOpenedApp(async (remoteMessage) => {
    trackOpened(remoteMessage.data);
    handleDeeplink(remoteMessage.data?.deeplink_url);
  });

  return unsubscribe;
}, []);

// Terminated tap
useEffect(() => {
  messaging()
    .getInitialNotification()
    .then((remoteMessage) => {
      if (remoteMessage) {
        trackOpened(remoteMessage.data);
        handleDeeplink(remoteMessage.data?.deeplink_url);
      }
    });
}, []);

const trackOpened = async (data: any) => {
  const deliveryId = data?.['Zixflow-Delivery-ID'] ?? '';
  const deliveryToken = data?.['Zixflow-Delivery-Token'] ?? cachedToken ?? '';

  if (deliveryId && deliveryToken) {
    await Zixflow.trackMetric({
      deliveryID: deliveryId,
      deviceToken: deliveryToken,
      event: MetricEvent.opened,
    });
  }
};
```

> **Note:** The SDK automatically tracks opens when using the default push implementation.

***

#### 3. Action Button Clicked

**When to track:** When the user taps a named action button

**SDK method:** `Zixflow.track({ name: "Push Notification Action Clicked", properties: {...} })`

**Implementation:**

```typescript theme={null}
import { Zixflow, MetricEvent } from 'zixflow-reactnative';

// Handle action press from local notification
const handleActionPress = async (data: any, actionId: string) => {
  const deliveryId = data['Zixflow-Delivery-ID'] ?? '';
  const token = data['Zixflow-Delivery-Token'] ?? '';
  const actionIndex = parseInt(actionId.replace(/\D/g, ''), 10);
  const buttons = parseButtons(data['action_buttons']);
  const actionName = buttons[actionIndex]?.name ?? `Action ${actionIndex + 1}`;
  const actionDeeplink = buttons[actionIndex]?.deeplink ?? '';

  // Always fire opened first
  await Zixflow.trackMetric({
    deliveryID: deliveryId,
    deviceToken: token,
    event: MetricEvent.opened,
  });

  // Then fire action clicked
  await Zixflow.track({
    name: 'Push Notification Action Clicked',
    properties: {
      'Zixflow-Delivery-ID': deliveryId,
      'Zixflow-Delivery-Token': token,
      'action_index': actionIndex,
      'action_name': actionName,
      'action_deeplink': actionDeeplink,
    },
  });
};

const parseButtons = (raw: any) => {
  if (!raw) return [];
  try {
    return typeof raw === 'string' ? JSON.parse(raw) : raw;
  } catch {
    return [];
  }
};
```

**Action button properties:**

| Property                 | Type    | Required    | Description                      |
| ------------------------ | ------- | ----------- | -------------------------------- |
| `Zixflow-Delivery-ID`    | string  | ✅           | Links event to campaign delivery |
| `action_index`           | integer | ✅           | 0-based index of button tapped   |
| `action_name`            | string  | ✅           | Button label (e.g., "Shop Now")  |
| `Zixflow-Delivery-Token` | string  | Recommended | FCM/APNs token                   |
| `action_deeplink`        | string  | Recommended | Button's URL                     |

***

### Action Buttons Format

The `action_buttons` field is a JSON-encoded string containing button definitions:

**Raw payload value:**

```
"[{\"name\":\"Shop Now\",\"deeplink\":\"https://yourapp.com/sale\"},{\"name\":\"Remind Me\",\"deeplink\":\"\"}]"
```

**Parsed structure:**

```json theme={null}
[
  { "name": "Shop Now",  "deeplink": "https://yourapp.com/sale" },
  { "name": "Remind Me", "deeplink": "" }
]
```

**Parsing example:**

```typescript theme={null}
const parseActionButtons = (raw: any): Array<{ name: string; deeplink: string }> => {
  if (!raw) return [];

  try {
    const decoded = typeof raw === 'string' ? JSON.parse(raw) : raw;
    return Array.isArray(decoded) ? decoded : [];
  } catch (error) {
    console.error('Error parsing action buttons:', error);
    return [];
  }
};

// Usage
const buttons = parseActionButtons(remoteMessage.data?.action_buttons);
buttons.forEach((btn, index) => {
  console.log(`Button ${index}: ${btn.name} -> ${btn.deeplink}`);
});
```

**Rules:**

* Maximum 2 buttons per notification (recommended for consistency across platforms)
* `deeplink` may be empty — handle gracefully
* Button index is 0-based (first button = index 0)

***

### Complete Push Tracking Implementation

Here's a complete example integrating all tracking events:

```typescript theme={null}
import React, { useEffect, useState } from 'react';
import messaging, { FirebaseMessagingTypes } from '@react-native-firebase/messaging';
import { Zixflow, MetricEvent } from 'zixflow-reactnative';

const App = () => {
  const [fcmToken, setFcmToken] = useState<string | null>(null);

  useEffect(() => {
    setupPushNotifications();
  }, []);

  const setupPushNotifications = async () => {
    // Request permission
    const authStatus = await messaging().requestPermission();
    const enabled =
      authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
      authStatus === messaging.AuthorizationStatus.PROVISIONAL;

    if (enabled) {
      // Get FCM token
      const token = await messaging().getToken();
      setFcmToken(token);

      // Register with Zixflow
      await Zixflow.registerDeviceToken({ deviceToken: token });

      // Listen for token refresh
      messaging().onTokenRefresh(async (newToken) => {
        setFcmToken(newToken);
        await Zixflow.registerDeviceToken({ deviceToken: newToken });
      });

      // Handle foreground messages
      messaging().onMessage(async (remoteMessage) => {
        trackDelivery(remoteMessage);
        showLocalNotification(remoteMessage);
      });

      // Handle background/quit taps
      messaging().onNotificationOpenedApp((remoteMessage) => {
        trackOpened(remoteMessage.data);
        handleDeeplink(remoteMessage.data?.deeplink_url);
      });

      // Handle terminated tap
      const initialNotification = await messaging().getInitialNotification();
      if (initialNotification) {
        trackOpened(initialNotification.data);
        handleDeeplink(initialNotification.data?.deeplink_url);
      }
    }
  };

  const trackDelivery = async (message: FirebaseMessagingTypes.RemoteMessage) => {
    const deliveryId = message.data?.['Zixflow-Delivery-ID'] ?? '';
    const deliveryToken = message.data?.['Zixflow-Delivery-Token'] ?? fcmToken ?? '';

    if (deliveryId && deliveryToken) {
      await Zixflow.trackMetric({
        deliveryID: deliveryId,
        deviceToken: deliveryToken,
        event: MetricEvent.delivered,
      });
    }
  };

  const trackOpened = async (data: any) => {
    const deliveryId = data?.['Zixflow-Delivery-ID'] ?? '';
    const deliveryToken = data?.['Zixflow-Delivery-Token'] ?? fcmToken ?? '';

    if (deliveryId && deliveryToken) {
      await Zixflow.trackMetric({
        deliveryID: deliveryId,
        deviceToken: deliveryToken,
        event: MetricEvent.opened,
      });
    }
  };

  const handleDeeplink = (url?: string) => {
    if (!url) return;
    // Implement your deep link navigation logic
    console.log('Navigating to:', url);
  };

  const showLocalNotification = async (message: FirebaseMessagingTypes.RemoteMessage) => {
    // Use local notification library to show notification
  };

  return (
    // Your app UI
    <></>
  );
};

export default App;
```

***

### Tracking Decision Flowchart

```
Notification received?
├── YES → trackMetric(delivered)        [automatic with SDK]
│
User interacted?
├── Tapped notification body
│   └── trackMetric(opened)             [automatic with SDK]
│       └── Navigate to deeplink_url
│
└── Tapped action button
    ├── trackMetric(opened)             [automatic with SDK]
    └── track("Push Notification Action Clicked")
        └── Navigate to button's deeplink
```

***

### Fallback for Non-Zixflow Push

If a push notification doesn't contain `Zixflow-Delivery-ID` (sent from another source), the SDK automatically skips tracking.

For custom analytics on non-Zixflow pushes, use a different event name:

```typescript theme={null}
// Don't use reserved push event names for non-Zixflow pushes
await Zixflow.track({
  name: 'External Push Received',
  properties: {
    'notification_id': message.messageId ?? '',
    'title': message.notification?.title ?? '',
    'body': message.notification?.body ?? '',
    'source': 'external',
  },
});
```

> **Important:** Push notification event names (`Push Notification Delivered`, `Push Notification Opened`, `Push Notification Action Clicked`) are reserved for Zixflow's delivery pipeline.

***

### Testing Push Tracking

#### Verify Tracking Events

Enable debug logging to see tracking events:

```typescript theme={null}
const config: ZixflowConfig = {
  apiKey: 'YOUR_API_KEY',
  logLevel: 'debug',
};

await Zixflow.setup(config);
```

Look for these log messages:

* `[Zixflow] Push Notification Delivered tracked`
* `[Zixflow] Push Notification Opened tracked`
* `[Zixflow] Push Notification Action Clicked tracked`

#### Check Campaign Analytics

1. Log into Zixflow dashboard
2. Navigate to **Messaging** → **Push Notifications**
3. View campaign metrics:
   * **Sent** - Total notifications sent
   * **Delivered** - Arrived on device
   * **Opened** - User tapped notification
   * **Clicked** - User tapped action button

***
