Pular para o conteúdo principal

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.

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

RequirementMinimum version
React Native>= 0.70.0
React>= 17.0.0
Node.js>= 16 LTS
npm or YarnVersion compatible with Node 16+
iOSCocoaPods support
AndroidAPI 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:

Configure the .npmrc file

In your project root (same directory as package.json), create or edit the .npmrc file:
//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:
echo ".npmrc" >> .gitignore
For CI/CD environments, set the token as an environment variable:
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

2. Package installation

With the token configured, install the SDK:
# 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:
# 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
DependencyMinimum versionPurpose
@react-native-async-storage/async-storage>= 1.17.0Local storage for session and attribution data
react-native-keychain>= 8.0.0Secure device identifier storage
react-native-device-info>= 10.0.0Retrieves device information

Native configuration

iOS

1. Install Pods

After installing JavaScript dependencies, run CocoaPods:
cd ios && pod install && cd ..
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:
<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

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):
<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:
<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:
<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

ParameterTypeRequiredDescription
apiKeystringYesAPI key obtained from the Nemu dashboard
uriSchemestringYesApp URI scheme (ex: "yourapp"). Must match the value configured in Smart Link
trackingIdstringYesTracking ID associated with the app in the Nemu dashboard
isDebugModebooleanNoEnables detailed console logs. Default: false
baseUrlstringNoOverrides the API base URL

Full initialization example

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:
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:
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

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

function onLogout() {
  NemuTracking.clearUserId();
}
Note: clearUserId() only removes the local association. Attribution history in the backend is preserved.

The SDK automatically handles two deep link types: 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
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. Register a callback to receive deep link data as soon as it is available:
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:
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

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?”
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).
NemuTracking.setSessionHistory({
  utm_source: 'newsletter',
  utm_medium: 'email',
  utm_campaign: 'black_friday_2026',
  utm_content: 'cta_header',
  utm_term: 'offers',
});
ParameterTypeRequiredDescription
utm_sourcestringYesCampaign source
utm_mediumstring | nullNoMedia/channel
utm_campaignstring | nullNoCampaign name
utm_contentstring | nullNoCreative content
utm_termstring | nullNoAdditional 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

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. 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

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:
// 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:
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:
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:
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:
    cd ios
    pod deintegrate
    pod install
    

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?

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