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
| 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:
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
| 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:
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:
- Open the
.xcworkspace project
- Select the app target
- Go to Signing & Capabilities
- Click + Capability and add Associated Domains
- 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.
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
| 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
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.
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:
- User clicks the Smart Link
- App opens with the URL
- SDK processes the URL and records the session
- 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:
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',
});
| 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
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
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:
- Verify that
react-native-keychain is installed as a JavaScript dependency
- Run:
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