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

# React Native SDK

> Nemu attribution and Smart Links SDK for React Native applications.

## Introduction

The **Nemu React Native SDK** (`@usenemu-account/react-native-sdk`) lets you integrate Nemu's attribution and Smart Links system directly into your React Native app.

With this SDK you can:

* **Capture install source** — know which campaign, source, or medium the user came from (UTMs)
* **Handle direct deep links** — when the user clicks a Smart Link and the app is already installed
* **Handle deferred deep links** — when the user clicks a Smart Link, installs the app from the store, and opens it for the first time
* **Identify users** — associate an ID from your system with the device to cross-reference attribution data
* **Query session history** — retrieve UTMs from the user's latest interaction

The SDK works automatically: once initialized, it intercepts deep links, detects whether this is the first app open, and performs attribution with Nemu's backend, without requiring manual configuration for each event.

***

## Prerequisites

| Requirement  | Minimum version                  |
| ------------ | -------------------------------- |
| React Native | >= 0.70.0                        |
| React        | >= 17.0.0                        |
| Node.js      | >= 16 LTS                        |
| npm or Yarn  | Version compatible with Node 16+ |
| iOS          | CocoaPods support                |
| Android      | API 21+ (Android 5.0)            |

> **Private package:** `@usenemu-account/react-native-sdk` is a restricted-access npm package. To install it, you need an **npm token** provided by the Nemu support team. See the installation section below.

***

## Installation

### 1. npm token setup

The `@usenemu-account/react-native-sdk` package is **private** and published in the npm registry with restricted access. Before installing it, you need to set up authentication.

#### Get your token

Contact Nemu support to receive your npm access token:

* **Email:** [suporte@nemu.com.br](mailto:suporte@nemu.com.br)
* **Website:** [nemu.com.br](https://usenemu.com)

#### Configure the `.npmrc` file

In your **project root** (same directory as `package.json`), create or edit the `.npmrc` file:

```bash theme={null}
//registry.npmjs.org/:_authToken=YOUR_NPM_TOKEN_HERE
```

Replace `YOUR_NPM_TOKEN_HERE` with the token provided by support.

> **Security:** add `.npmrc` to your `.gitignore` so the token is not committed to the repository:
>
> ```bash theme={null}
> echo ".npmrc" >> .gitignore
> ```
>
> For CI/CD environments, set the token as an environment variable:
>
> ```bash theme={null}
> //registry.npmjs.org/:_authToken=${NPM_TOKEN}
> ```

### 2. Package installation

With the token configured, install the SDK:

```bash theme={null}
# npm
npm install @usenemu-account/react-native-sdk

# or Yarn
yarn add @usenemu-account/react-native-sdk
```

### 3. Required dependencies (peer dependencies)

The SDK requires the following libraries as peer dependencies. Install them if they are not already in your project:

```bash theme={null}
# npm
npm install @react-native-async-storage/async-storage react-native-keychain react-native-device-info

# or Yarn
yarn add @react-native-async-storage/async-storage react-native-keychain react-native-device-info
```

| Dependency                                  | Minimum version | Purpose                                        |
| ------------------------------------------- | --------------- | ---------------------------------------------- |
| `@react-native-async-storage/async-storage` | >= 1.17.0       | Local storage for session and attribution data |
| `react-native-keychain`                     | >= 8.0.0        | Secure device identifier storage               |
| `react-native-device-info`                  | >= 10.0.0       | Retrieves device information                   |

***

## Native configuration

### iOS

#### 1. Install Pods

After installing JavaScript dependencies, run CocoaPods:

```bash theme={null}
cd ios && pod install && cd ..
```

#### 2. Configure Universal Links (required for direct deep links)

For iOS to route Smart Links directly to the app (without opening the browser), you need to configure **Associated Domains**.

In Xcode:

1. Open the `.xcworkspace` project
2. Select the app target
3. Go to **Signing & Capabilities**
4. Click **+ Capability** and add **Associated Domains**
5. Add the domain in this format:

```
applinks:your-domain.nemu.com.br
```

> The exact domain will be provided by the Nemu team along with your credentials.

#### 3. Configure URI Scheme (required)

In the `Info.plist` file, add your app URI scheme:

```xml theme={null}
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>yourapp</string>
    </array>
  </dict>
</array>
```

Replace `yourapp` with the scheme defined in the Nemu dashboard for your Smart Link.

***

### Android

#### 1. Configure Deep Links in AndroidManifest.xml

For Smart Links to open the app directly, add intent filters to your main `Activity`.

In `android/app/src/main/AndroidManifest.xml`, inside the main `<activity>` tag:

**App Links (Android Universal Links):**

```xml theme={null}
<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data
    android:scheme="https"
    android:host="your-domain.nemu.com.br" />
</intent-filter>
```

**URI Scheme:**

```xml theme={null}
<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="yourapp" />
</intent-filter>
```

Replace `your-domain.nemu.com.br` and `yourapp` with values matching your project.

#### 2. Internet permission (usually already present)

Verify that `AndroidManifest.xml` includes internet permission:

```xml theme={null}
<uses-permission android:name="android.permission.INTERNET" />
```

> In most React Native projects this permission is already included by default.

***

## Initialization

SDK initialization must be done **only once**, in your app root component (usually `App.tsx`), inside a `useEffect`.

### Configuration parameters

| Parameter     | Type      | Required | Description                                                                     |
| ------------- | --------- | -------- | ------------------------------------------------------------------------------- |
| `apiKey`      | `string`  | Yes      | API key obtained from the Nemu dashboard                                        |
| `uriScheme`   | `string`  | Yes      | App URI scheme (ex: `"yourapp"`). Must match the value configured in Smart Link |
| `trackingId`  | `string`  | Yes      | Tracking ID associated with the app in the Nemu dashboard                       |
| `isDebugMode` | `boolean` | No       | Enables detailed console logs. Default: `false`                                 |
| `baseUrl`     | `string`  | No       | Overrides the API base URL                                                      |

### Full initialization example

```typescript theme={null}
import React, { useEffect } from 'react';
import { NemuTracking } from '@usenemu-account/react-native-sdk';

function App() {
  useEffect(() => {
    NemuTracking.init({
      apiKey: 'your-api-key',
      uriScheme: 'yourapp',
      trackingId: 'your-tracking-id',
    });
  }, []);

  return (
    // ... your app
  );
}

export default App;
```

### Using environment variables

To avoid exposing credentials directly in code, use `react-native-config` or a similar package:

```typescript theme={null}
import Config from 'react-native-config';
import { NemuTracking } from '@usenemu-account/react-native-sdk';

useEffect(() => {
  NemuTracking.init({
    apiKey: Config.NEMU_API_KEY!,
    uriScheme: Config.NEMU_URI_SCHEME!,
    trackingId: Config.NEMU_TRACKING_ID!,
  });
}, []);
```

With the corresponding `.env`:

```env theme={null}
NEMU_API_KEY=your-api-key
NEMU_URI_SCHEME=yourapp
NEMU_TRACKING_ID=your-tracking-id
```

***

## User identification

After user login in your app, associate their ID with the device. This allows Nemu to correlate attribution data with your user system.

### Register user

```typescript theme={null}
import { NemuTracking } from '@usenemu-account/react-native-sdk';

// After successful login
function onLoginSuccess(userId: string) {
  NemuTracking.setUserId(userId);
}
```

The SDK stores the ID locally and sends the association to the backend automatically.

### Clear user on logout

```typescript theme={null}
function onLogout() {
  NemuTracking.clearUserId();
}
```

> **Note:** `clearUserId()` only removes the local association. Attribution history in the backend is preserved.

***

## Deep Links

The SDK automatically handles two deep link types:

### Direct deep links

They occur when the **app is already installed** and the user clicks a Smart Link. The operating system opens the app directly (via Universal Link / App Link or URI scheme).

The flow is transparent:

1. User clicks the Smart Link
2. App opens with the URL
3. SDK processes the URL and records the session
4. Listeners registered with `onDeepLink` are notified with the data

### Deferred deep links

They occur when the **app is not installed**. The user clicks the Smart Link, is redirected to the store, installs the app, and opens it for the first time.

The SDK automatically detects this scenario on first open and returns attribution and deep link data through `onDeepLink` with `isDeferred: true`.

### Reactive listener (`onDeepLink`)

Register a callback to receive deep link data as soon as it is available:

```typescript theme={null}
import React, { useEffect } from 'react';
import { NemuTracking } from '@usenemu-account/react-native-sdk';
import type { DeepLinkData } from '@usenemu-account/react-native-sdk';

function App() {
  useEffect(() => {
    NemuTracking.init({
      apiKey: 'your-api-key',
      uriScheme: 'yourapp',
      trackingId: 'your-tracking-id',
    });

    const unsubscribe = NemuTracking.onDeepLink((data: DeepLinkData) => {
      console.log('Deep link received:', data);

      if (data.deepLinkValue) {
        // Navigate to the matching screen
        // Ex: data.deepLinkValue = "product/456"
        navigateTo(data.deepLinkValue);
      }

      if (data.utms) {
        console.log('Source:', data.utms.source);
        console.log('Campaign:', data.utms.campaign);
      }

      console.log('Was deferred?', data.isDeferred);
    });

    // Clear listener on unmount
    return () => unsubscribe();
  }, []);

  return (/* ... */);
}
```

> **Automatic replay:** if a deep link was already processed before the listener was registered, the callback is triggered immediately with the latest data. This prevents the app from losing the initial deep link.

### Imperative query (`getDeepLinkData`)

If you prefer querying data imperatively instead of using the listener:

```typescript theme={null}
async function checkDeepLink() {
  const data = await NemuTracking.getDeepLinkData();

  if (data) {
    console.log('App opened via Smart Link:', data.deepLinkValue);
  } else {
    console.log('App opened organically');
  }
}
```

### `DeepLinkData` structure

```typescript theme={null}
interface DeepLinkData {
  /** Deep link value (ex: "product/456", "category/shoes") */
  deepLinkValue: string | null;

  /** true = deferred deep link (user installed after clicking) */
  isDeferred: boolean;

  /** Smart Link UTM parameters */
  utms: {
    source: string | null;
    medium: string | null;
    campaign: string | null;
    content: string | null;
    term: string | null;
  } | null;
}
```

***

## Attribution

### Last session history (last touch)

Query UTMs from the user's most recent interaction. It answers: **"what were the UTMs from the last visit?"**

```typescript theme={null}
async function checkLastSession() {
  const session = await NemuTracking.getLastSessionHistory();

  if (session) {
    console.log('Source:', session.utm_source);
    console.log('Medium:', session.utm_medium);
    console.log('Campaign:', session.utm_campaign);
    console.log('Content:', session.utm_content);
    console.log('Term:', session.utm_term);
  } else {
    console.log('No session found');
  }
}
```

### Manual UTM history insertion (`setSessionHistory`)

Use `setSessionHistory` when you need to record UTMs manually in custom flows (for example, backend-defined campaigns, internal onboarding, or business rules that do not depend on deep links).

```typescript theme={null}
NemuTracking.setSessionHistory({
  utm_source: 'newsletter',
  utm_medium: 'email',
  utm_campaign: 'black_friday_2026',
  utm_content: 'cta_header',
  utm_term: 'offers',
});
```

| Parameter      | Type             | Required | Description      |
| -------------- | ---------------- | -------- | ---------------- |
| `utm_source`   | `string`         | Yes      | Campaign source  |
| `utm_medium`   | `string \| null` | No       | Media/channel    |
| `utm_campaign` | `string \| null` | No       | Campaign name    |
| `utm_content`  | `string \| null` | No       | Creative content |
| `utm_term`     | `string \| null` | No       | Additional term  |

Important behavior:

* `utm_source` is required; invalid calls are safely ignored
* The method reuses the SDK's internal session/history flow (`trackEvent`)
* There is deduplication to avoid excessive history creation in repeated calls
* Execution is asynchronous and resilient (internal failures should not break the app)

> **When to use:** if the source already came from a Smart Link/deep link with valid UTMs, prefer the automatic flow. Use `setSessionHistory` to complement scenarios not covered by the link.

### Example: attaching UTMs to a purchase

```typescript theme={null}
async function trackPurchase(orderId: string, amount: number) {
  const lastSession = await NemuTracking.getLastSessionHistory();

  const purchasePayload = {
    orderId,
    amount,
    lastSource: lastSession?.utm_source ?? null,
    lastCampaign: lastSession?.utm_campaign ?? null,
    lastMedium: lastSession?.utm_medium ?? null,
    lastContent: lastSession?.utm_content ?? null,
    lastTerm: lastSession?.utm_term ?? null,
  };

  // Send to your backend
  await fetch('https://your-api.com/purchases', {
    method: 'POST',
    body: JSON.stringify(purchasePayload),
  });
}
```

***

## WebView integration

When the app embeds web content via WebView and the page needs to consume attribution data (for example, the UTMs returned by `getLastSessionHistory`), the SDK only runs on the native (React Native) side. For the web page to access this data, you need to create a **bidirectional bridge** between React Native and the WebView.

### Recommended strategy

The most reliable approach combines two mechanisms:

* **Pre-injection**: data is exposed as a global variable in the WebView before the content loads
* **On-demand bridge**: the web page can request a fresh version of the history at any time

> **Why not use URL query params?** It is the simplest approach, but it has size limits, exposes data in the URL, and does not allow refreshing values without reloading the page. Use it only when data is small and static.

### React Native implementation

```tsx theme={null}
import React, { useEffect, useRef, useState } from 'react';
import { WebView, type WebViewMessageEvent } from 'react-native-webview';
import { NemuTracking } from '@usenemu-account/react-native-sdk';
import type { TrackingSessionHistory } from '@usenemu-account/react-native-sdk';

function ScreenWithWebView() {
  const webRef = useRef<WebView>(null);
  const [initialHistory, setInitialHistory] =
    useState<TrackingSessionHistory | null | undefined>(undefined);

  useEffect(() => {
    NemuTracking.getLastSessionHistory().then(setInitialHistory);
  }, []);

  // Wait for the initial query before mounting the WebView
  if (initialHistory === undefined) return null;

  const preload = `
    window.__NEMU_HISTORY__ = ${JSON.stringify(initialHistory)};
    window.nemuRequestHistory = function () {
      window.ReactNativeWebView.postMessage(
        JSON.stringify({ type: 'NEMU_GET_HISTORY' })
      );
    };
    true;
  `;

  const onMessage = async (event: WebViewMessageEvent) => {
    try {
      const msg = JSON.parse(event.nativeEvent.data);
      if (msg.type === 'NEMU_GET_HISTORY') {
        const history = await NemuTracking.getLastSessionHistory();
        webRef.current?.injectJavaScript(`
          window.dispatchEvent(new CustomEvent('nemu:history', {
            detail: ${JSON.stringify(history)}
          }));
          true;
        `);
      }
    } catch {
      // Ignore messages outside of the protocol
    }
  };

  return (
    <WebView
      ref={webRef}
      source={{ uri: 'https://your-page.com' }}
      injectedJavaScriptBeforeContentLoaded={preload}
      onMessage={onMessage}
    />
  );
}
```

### Consuming on the web side

Inside the page loaded in the WebView, data is available right at load time and can be refreshed on demand:

```js theme={null}
// Immediate access on page load
const history = window.__NEMU_HISTORY__;
console.log('Last session UTMs:', history);

// Receive updates when React Native responds to a request
window.addEventListener('nemu:history', (event) => {
  console.log('Updated history:', event.detail);
});

// Request a fresh version
window.nemuRequestHistory();
```

### Important considerations

* **Wait for SDK `init`** before mounting the WebView. Calling `getLastSessionHistory()` before `NemuTracking.init()` causes the Promise to be rejected.
* **JSON sanitization**: when injecting data into the WebView, always use `JSON.stringify`. Never interpolate strings directly to avoid XSS if any field comes from an external source.
* **`injectedJavaScriptBeforeContentLoaded`**: requires `react-native-webview` >= 11. In older versions, use `injectedJavaScript` (runs after DOM load — a race condition with page scripts may occur).
* **Environment detection**: if the same web page also runs outside the WebView (in a browser), treat `window.ReactNativeWebView` as optional to avoid errors:

```js theme={null}
const isInWebView = typeof window.ReactNativeWebView !== 'undefined';

if (isInWebView) {
  window.nemuRequestHistory();
}
```

***

## Advanced usage

### Debug mode

Enable debug mode to view detailed logs of all SDK operations in the console:

```typescript theme={null}
NemuTracking.init({
  apiKey: 'your-api-key',
  uriScheme: 'yourapp',
  trackingId: 'your-tracking-id',
  isDebugMode: true,
});
```

Logs will appear prefixed with `[NemuSDK]` and include information about:

* Deep link processing
* Initialization flow
* Errors and network failures

> **Important:** disable debug mode in production. Logs may contain sensitive information.

### Base URL override

In development or staging environments, you can point the SDK to a different API:

```typescript theme={null}
NemuTracking.init({
  apiKey: 'your-api-key',
  uriScheme: 'yourapp',
  trackingId: 'your-tracking-id',
  isDebugMode: true,
  baseUrl: 'https://staging-trackings.nemu.com.br',
});
```

> `baseUrl` is only used when `isDebugMode` is `true`. In production mode, the SDK always uses the default URL.

***

## Troubleshooting

### npm authentication error during installation

**Error:**

```
npm ERR! 401 Unauthorized - GET https://registry.npmjs.org/@usenemu%2freact-native-sdk
```

**Solution:**

* Check whether the `.npmrc` file exists in the project root
* Confirm the token is correct and has not expired
* Contact Nemu support to validate or renew the token

***

### Error "NemuTracking.init() must be called before using any other method"

**Cause:** an SDK method was called before initialization.

**Solution:** ensure `NemuTracking.init()` is called in the root component `useEffect` **before** any other SDK method.

***

### `pod install` fails on iOS

**Error:**

```
[!] Unable to find a specification for `react-native-keychain`
```

**Solution:**

1. Verify that `react-native-keychain` is installed as a JavaScript dependency
2. Run:
   ```bash theme={null}
   cd ios
   pod deintegrate
   pod install
   ```

***

### Deep links do not work on iOS

**Checks:**

* Is Associated Domain configured correctly in Xcode? (format: `applinks:your-domain`)
* Is the `apple-app-site-association` file published and accessible on the domain? (configured by the Nemu team)
* Is the URI scheme declared in `Info.plist`?

***

### Deep links do not work on Android

**Checks:**

* Are the intent filters correct in `AndroidManifest.xml`?
* Is automatic domain verification enabled (`android:autoVerify="true"`)?
* Is the `assetlinks.json` file published on the domain? (configured by the Nemu team)

***

### No attribution data returned

**Checks:**

* Are `apiKey` and `trackingId` correct?
* Is the Smart Link configured and active in the Nemu dashboard?
* Test with `isDebugMode: true` and check `[NemuSDK]` logs in the console

***
