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

# React Native SDK

> Hightouch's React Native SDK for collecting events

The React Native SDK makes it easy to track events in React Native
applications. The SDK supports both iOS and Android applications.

## Installation

The React Native SDK is distributed via `npm`.

Install the required dependencies into your React Native project via `npm`:

```
npm install --save \
  @ht-sdks/events-sdk-react-native \
  @ht-sdks/sovran-react-native \
  react-native-get-random-values \
  @react-native-async-storage/async-storage
```

### iOS

On iOS, you will also need to install the native modules by running:

```
npx pod-install
```

### Android

The SDK requires additional permissions on Android in order to populate the
device context for events.

Add these permissions to your app by updating your `AndroidManifest.xml`:

```
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
```

## SDK Initialization

Once the dependencies are installed, you can import the tracking client into
your application.

```
import { createClient } from '@ht-sdks/events-sdk-react-native';

const htClient = createClient({
  writeKey: 'API_KEY'
});
```

The client can either be passed around explicitly throughout your app, or
exposed through a [React context
hook](https://react.dev/learn/passing-data-deeply-with-context). To pass the
context through a Context, use `AnalyticsProvider` and `useAnalytics`.

```
import { AnalyticsProvider } from '@ht-sdks/events-sdk-react-native';

// The Child component can retrieve the htClient by calling `useAnalytics()`.
const App = () => (
  <AnalyticsProvider client={htClient}>
    <Child />
  </AnalyticsProvider>
);
```

The client may be configured with the following options:

| Parameter | Type | Description |
| - | - | - |
| `writeKey` | String | The write key used to identify the source in Hightouch. |
| `collectDeviceId` | Boolean | Whether to autocollect a device ID via the Android DRM API. The ID is stored in `context.device.id` on all events. This is only supported on Android, and is disabled by default. |
| `debug` | Boolean | Whether to generate debug logs. Defaults to `true`. |
| `logger` | Logger | Custom logger instance to redirect internal logging from the library. |
| `flushAt` | Number | Maximum number of events to accumulate before sending to the Hightouch API. Defaults to `20`. |
| `flushInterval` | Number | Maximum number of seconds to hold events before sending to the Hightouch API. Defaults to `30`. |
| `flushPolicies` | Array | Optional advanced customization for how events are buffered before sending to the Hightouch API. Mutually exclusive with `flushAt` and `flushInterval` — if set, those properties are ignored. |
| `maxBatchSize` | Number | The maximum number of events to send in one HTTP request to the Hightouch API. You may want to adjust this if your events are extremely large. Defaults to `1000`. |
| `trackAppLifecycleEvents` | Boolean | Whether to automatically track lifecycle events like `Application Installed`. Defaults to `false`. |
| `trackDeepLinks` | Boolean | Whether to automatically track deep links. Only supported for Android. Defaults to `false`. |
| `proxy` | String | Used to override the URL used for sending events to Hightouch. This should contain the full path, e.g. `https://us-east-1.hightouch-events.com/v1/batch` |
| `foregroundSessionTimeout` | Number | Maximum foreground inactivity in milliseconds before a new session starts. Defaults to `1800000` (30 minutes). |
| `backgroundSessionTimeout` | Number | Maximum time in the background, in milliseconds, before a new session starts on resume. Defaults to `1800000` (30 minutes). |

## Session tracking

Session tracking is enabled by default. The SDK attaches `sessionId`, `sessionStart`, and a `session` object to `context` on every event so you can group events from the same app session.

Sessions use two timeouts, both in milliseconds:

* `foregroundSessionTimeout` limits inactivity while the app is in the foreground.
* `backgroundSessionTimeout` limits how long the app can stay in the background before the next resume starts a new session.

Both default to `1800000` (30 minutes). Set both to `0` to disable session tracking.

A new session starts when any of these occur:

* First launch, when no session state is persisted.
* Foreground inactivity exceeds `foregroundSessionTimeout`.
* Resume after being backgrounded longer than `backgroundSessionTimeout`, including after process death.
* Calling `reset()`.

Session state is persisted across app relaunches. `sessionIndex` is a per-device lifetime counter. It starts at `0` and resets only when the app is uninstalled, not when you call `reset()`.

You can change the timeouts when you initialize the SDK:

```
const htClient = createClient({
  writeKey: 'API_KEY',
  foregroundSessionTimeout: 60 * 60 * 1000,
  backgroundSessionTimeout: 60 * 60 * 1000,
});
```

`context.session` includes:

| Field | Type | Description |
| - | - | - |
| `sessionId` | int | Current session ID (epoch milliseconds). |
| `sessionIndex` | int | Per-device lifetime counter. Starts at `0`. |
| `sessionStart` | boolean | Present and `true` only on the first event of the session. |
| `eventIndex` | int | 0-based index of the event within the session. |
| `previousSessionId` | int or null | Previous session ID. `null` on the first session. |
| `firstEventId` | UUID | `messageId` of the session's first event. |
| `firstEventTimestamp` | ISO 8601 | When the session began. |

`sessionId` and `sessionStart` are also set on `context` for parity with the [browser SDK](https://docs.hightouch.com/events/sdks/browser.md#session-tracking). `sessionStart` is included only when it is `true`.

## Manual tracking API

### Identify

The `identify` method is used to send an `identify` event.

If `identify` is called multiple times for the same user, the `traits` are
merged together.

**Method signature**:

```
htClient.identify(userId, [traits])
```

**Method parameters**:

| Parameter | Type | Description |
| - | - | - |
| `userId` | String | The user's persistent ID. |
| `traits` | Object | Additional traits about the user, such as `email` and `name`. |

### Track

The `track` method is used to send a `track` event.

**Method signature**:

```
htClient.track(event, [properties])
```

**Method parameters**:

| Parameter | Type | Description |
| - | - | - |
| `event` | String | The event name (for example "Checked Out"). |
| `properties` | Object | Additional properties about the event, such as `product_id`. |

### Screen

The `screen` method is used to send a `screen` event.

**Method signature**:

```
htClient.screen(name, [properties])
```

**Method parameters**:

| Parameter | Type | Description |
| - | - | - |
| `name` | String | The screen's name. For example, "Getting started". |
| `properties` | Object | Additional properties about the event. |

### Group

The `group` method sends a `group` event.

**Method signature**:

```
htClient.group(groupId, [traits])
```

**Method parameters**:

| Parameter | Type | Description |
| - | - | - |
| `groupId` | String | The id for the group. |
| `traits` | Object | Additional traits about the group, such as `company_name`. |

### Reset

The `reset` method resets the `identify` calls for the local session.
Specifically, it resets the `anonymousId`, `userId`, and `traits`.

The `reset` method should be called when users log out. This way, if the user
logs back in with another account, the userIds and traits from the different
sessions remain separate. Calling `reset()` also starts a new analytics session.
See [Session tracking](#session-tracking).

**Method signature**:

```
htClient.reset()
```

### Flush

The `flush` method forces the SDK to send all queued events to the Hightouch API immediately, rather than waiting for the next automatic flush cycle.

**Method signature**:

```
await htClient.flush()
```

Call `flush` after mission-critical events like purchases or post-login `identify` calls to make sure those events are delivered before the user closes or backgrounds the app.

```ts theme={null}
// Ensure a purchase event is sent immediately
htClient.track("Order Completed", { orderId: "12345", total: 59.99 });
await htClient.flush();
```

#### Flush behavior

The SDK queues events locally and sends them to the Hightouch API in batches. By default, the SDK flushes when either of these conditions is met:

* **Count threshold** (`flushAt`): The queue accumulates 20 events (configurable via the `flushAt` option in `createClient`).
* **Time interval** (`flushInterval`): 30 seconds have elapsed since the last flush (configurable via the `flushInterval` option in `createClient`).

Events on the queue are persisted to `AsyncStorage`, so they survive app restarts. Only events that are successfully delivered are removed from the queue. Failed events are retried on the next flush cycle.

#### Flush policies

For more control over when batches are sent, you can pass custom flush policies via the `flushPolicies` option in `createClient`. The SDK ships with the following built-in policies:

* `CountFlushPolicy(limit)` — flushes after a specified number of events accumulate. This is the policy behind the `flushAt` default.
* `TimerFlushPolicy(intervalMs)` — flushes on a recurring time interval (in milliseconds). This is the policy behind the `flushInterval` default.
* `BackgroundFlushPolicy()` — flushes automatically when the app is sent to the background. This is useful for ensuring events are sent before the OS suspends your app.
* `StartupFlushPolicy()` — triggers a single flush immediately when the client starts. This is useful for sending any events that were queued from a previous session.

```
import {
  createClient,
  CountFlushPolicy,
  TimerFlushPolicy,
  BackgroundFlushPolicy,
  StartupFlushPolicy,
} from "@ht-sdks/events-sdk-react-native";

const htClient = createClient({
  writeKey: "API_KEY",
  flushPolicies: [
    new CountFlushPolicy(10), // Flush every 10 events
    new TimerFlushPolicy(15 * 1000), // Flush every 15 seconds
    new BackgroundFlushPolicy(), // Flush when the app is backgrounded
    new StartupFlushPolicy(), // Flush any queued events on startup
  ],
});
```

> [!WARNING]
>
> `flushPolicies` is mutually exclusive with `flushAt` and `flushInterval`. If
> you provide `flushPolicies`, the SDK ignores `flushAt` and `flushInterval`
> entirely and uses only the policies you specify. The default count and timer
> policies are not included automatically, so add them explicitly if you still
> want that behavior.

You can also add or remove flush policies at runtime:

```ts theme={null}
const backgroundPolicy = new BackgroundFlushPolicy();

// Add a policy after initialization
htClient.addFlushPolicy(backgroundPolicy);

// Remove a policy later
htClient.removeFlushPolicy(backgroundPolicy);
```

## Anonymous ID management

Every device that uses the SDK is assigned an `anonymousId` — a UUID that the SDK auto-generates on first launch and persists in `AsyncStorage`. The `anonymousId` is attached to every event, allowing Hightouch to stitch anonymous activity to a known user once `identify` is called.

The anonymous ID and other user info are accessible through the `userInfo` property on the client.

### Get the anonymous ID

To read the current `anonymousId`, call `userInfo.get()`. Pass `true` for a safe async read that waits for storage to be ready:

```ts theme={null}
// Async (recommended) — waits for storage to be fully restored
const { anonymousId } = await htClient.userInfo.get(true);

// Sync — returns the current in-memory value immediately
const { anonymousId } = htClient.userInfo.get();
```

This is useful when you need to pass the anonymous ID to another system or include it in a deep link for cross-platform identity resolution.

### Set or override the anonymous ID

To replace the auto-generated `anonymousId` with your own value, use `userInfo.set()`:

```ts theme={null}
await htClient.userInfo.set((state) => ({
  ...state,
  anonymousId: "my-custom-anonymous-id",
}));
```

Common use cases for overriding the anonymous ID include:

* **Migrating from another vendor.** If your app previously used a different analytics SDK, you can carry over the existing anonymous ID so that pre- and post-migration events are attributed to the same device.
* **Using your own device identifier.** If your app already generates a stable device ID, you can use it as the anonymous ID to simplify identity resolution.

> [!TIP]
>
> Set the anonymous ID before any `identify` or `track` calls so that all events
> share the same identifier from the start of the session.

### Relationship between anonymousId and userId

Before `identify` is called, all events carry only the `anonymousId`. Once you call `identify(userId)`, subsequent events carry both the `anonymousId` and the `userId`. Hightouch uses both values to stitch anonymous and identified activity together during identity resolution.

Calling `reset()` generates a new `anonymousId` and clears the `userId` and `traits`, starting a fresh anonymous session. If you want to keep the current `anonymousId` while clearing the user, pass `false`:

```ts theme={null}
// Reset everything including anonymousId (default)
await htClient.reset();

// Clear userId and traits but keep the current anonymousId
await htClient.reset(false);
```

## Out of the box events

### Lifecycle events

When `trackAppLifecycleEvents` is enabled, Hightouch automatically tracks the following events:

* `Application Installed` -- Emitted when the app is first opened after a new install.
* `Application Updated` -- Emitted when the app is first opened after upgrading from a previous version.
* `Application Opened` -- Emitted whenever the app is opened (including when it's resuming from the background).
* `Application Backgrounded` -- Emitted whenever the app is backgrounded.

## Prebuilt plugins

Use Hightouch's prebuilt plugins to add integrations without writing a plugin yourself. Install the package for each plugin you need, then register it with your Hightouch client.

### Braze

Use the Braze plugin to forward Hightouch calls to the native Braze SDKs. This lets Braze evaluate in-app message triggers while the user is active, alongside normal event collection in Hightouch.

The setup below requires Braze plugin version `1.0.0` or later. Plugin version `1.0.0` requires Hightouch React Native SDK `3.x` and Braze React Native SDK `5.x`. Upgrade your Hightouch SDK if needed, then install the plugin and Braze SDK:

```bash theme={null}
npm install @ht-sdks/events-sdk-react-native@^3 @ht-sdks/events-sdk-react-native-plugin-braze@^1 @braze/react-native-sdk@^5
npx pod-install
```

Complete Braze's native iOS and Android initialization before sending events. Configure the Braze SDK API keys and endpoints in that native setup, rather than passing them to the Hightouch plugin in JavaScript.

Enable the `Appboy` integration and register the plugin when creating your Hightouch client:

```jsx theme={null}
import { createClient } from "@ht-sdks/events-sdk-react-native";
import { BrazePlugin } from "@ht-sdks/events-sdk-react-native-plugin-braze";

const htClient = createClient({
  writeKey: "YOUR_WRITE_KEY",
  defaultSettings: { integrations: { Appboy: {} } },
});

htClient.add({ plugin: new BrazePlugin({ forwardScreenViews: true }) });
```

`Appboy` is the integration key for Braze. Without this settings entry, the plugin does not forward events.

The plugin forwards `identify` calls as user updates and `track` calls as custom events. By default, `Order Completed` and `Completed Order` become purchases, with one purchase per product. Screen forwarding is off by default; `forwardScreenViews: true` forwards `screen` calls as custom events.

Your application configures Braze sessions, message presentation, Content Cards, and push notifications through the native Braze SDKs. See the [Braze example](https://github.com/ht-sdks/events-sdk-react-native/tree/master/examples/BrazeExample) for native setup. If you're replacing an mParticle kit, follow Migrate the mParticle Braze kit.

### Advertising identifiers

To collect IDFAs and AAIDs for advertising identification, you may use the
`@ht-sdks/events-sdk-react-native-plugin-advertising-id` and
`@ht-sdks/events-sdk-react-native-plugin-idfa` packages.

These plugins load native modules that pull the local advertising ID.

#### Android AAID

Once setup, the AAID will be stored in `context.device.advertisingId`

1. Add the Android AAID plugin to your project.

   ```
   npm install @ht-sdks/events-sdk-react-native-plugin-advertising-id
   ```

2. Add the plugin to your code by adding the following code after initializing your client.

   ```
   htClient.add({plugin: new AdvertisingIdPlugin()});
   ```

3. Update your Android application manifest to include your [AdMob app ID](https://developers.google.com/admob/android/quick-start).

   ```
   <manifest>
     <application>
       <!-- Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713 -->
       <meta-data
           android:name="com.google.android.gms.ads.APPLICATION_ID"
           android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy"/>
     </application>
   </manifest>
   ```

#### iOS IDFA

Once setup, the IDFA will be stored in `context.device.advertisingId`

1. Add the iOS IDFA plugin to your project.

   ```
   npm install @ht-sdks/events-sdk-react-native-plugin-idfa
   ```

2. Add the plugin to your code by adding the following code after initializing your client.

   ```
   htClient.add({plugin: new IdfaPlugin()});
   ```

3. Update your `Info.plist` to set
   [NSUserTrackingUsageDescription](https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription).
   This description will be displayed when prompting users for permission to
   track their IDFA.

## Tips and troubleshooting

### Missing events

If events are not appearing in Hightouch, work through the following steps:

1. **Upgrade to the latest SDK version.** Run `npm install @ht-sdks/events-sdk-react-native@latest` to pick up the most recent bug fixes and delivery improvements.

2. **Add a manual `flush` after critical events.** The SDK buffers events for up to 30 seconds by default. Although queued events are persisted and retried on the next app launch, calling `await htClient.flush()` immediately after high-value events like purchases or sign-ups ensures they are delivered promptly rather than waiting for the next flush cycle or app session.

3. **Add a `BackgroundFlushPolicy`.** This ensures the queue is flushed whenever the app moves to the background, covering cases where users leave the app between automatic flush cycles. You can add it at runtime without replacing your existing policies:

   ```
   import { BackgroundFlushPolicy } from "@ht-sdks/events-sdk-react-native";

   htClient.addFlushPolicy(new BackgroundFlushPolicy());
   ```

4. **Enable debug logging.** Set `debug: true` in `createClient` (this is the default) and check device logs for errors during event delivery.

5. **Verify your write key.** Confirm that the `writeKey` in `createClient` matches the write key shown on your event source in Hightouch.
