React Native SDK
Integrate deep linking and mobile attribution into your React Native app with the LinkForty SDK.
If you're building with Expo, use the Expo SDK instead. It provides the same features with a pure-JS implementation and no native code required.
Using Claude Code, Cursor or Codex? Copy this prompt and paste it into your agent from the root of your project. It installs the SDK, wires deep links through your router (asking you which screen each link should open), adds one event, and verifies the install in your LinkForty dashboard.
Show the prompt
I use LinkForty (https://linkforty.com), a deep-linking and mobile attribution platform: short links that route each click to the app, the app store or the web, and record clicks, installs and in-app events per link.
Before writing any code, read the LinkForty documentation index at https://docs.linkforty.com/llms-full.txt and the pages listed under "Docs" below. Work from what the docs say, not from assumptions about how similar products work. If something you need is not in the docs or in this prompt — an ID, a token, a URL — stop and ask me for it rather than inventing a placeholder that looks real.
## Task: install the LinkForty React Native SDK
Add the LinkForty SDK to this app so that an install that follows a click on one of my links is attributed to that link, a link's deep-link path opens the right screen on first launch (deferred deep linking) and on later opens (Universal Links / App Links), and one key in-app event is recorded.
## Values
- **App token**: shown in the LinkForty dashboard under Workspace Settings → App Token (it starts with `at_`). Ask me for it; put it in the app's config or environment, not hard-coded in a committed source file.
- **Base URL**: `https://api.linkforty.com` for LinkForty Cloud. (Only a self-hosted LinkForty server uses a different origin — ask if you are not sure which this is.)
- **Link host**: the host my links are on, shown in the dashboard under Settings → Domains — the workspace's own host (it looks like `something.toapp.me`) and any verified custom domain listed there. Ask me for it. This is the domain the app must claim for Universal Links / App Links.
- **LinkForty MCP server** (optional): if it is configured in this environment (https://mcp.linkforty.com/mcp, or the `linkforty-mcp` command), use `get_app_config` to read the workspace's app settings and `create_link` to make a test link at the end. Do not set it up for me if it is not.
## How to work
1. **Look before you change anything.** Identify the framework, its version, the navigation/router library and where the app initializes at launch. List the screens (routes) you found, and **ask me which screen each incoming link should open and what parameter it needs** — for example "a link with `productId` opens the product screen". Wait for my answer before editing; do not guess routes.
2. **Work on a new git branch.** If the working tree has uncommitted changes, stop and tell me instead of building on top of them.
3. **Follow the docs pages named below, not memory.** Package names, method names and config keys come from those pages; LinkForty's SDKs are not the same as other attribution SDKs you may know.
4. **Do not invent values.** Anything in "Values" that says "ask me" is something you ask for. Never commit a token to source.
5. **Keep the app working without a link.** A launch with no matching click must behave exactly as before: no crash, no error surfaced to the user, no blocked start-up.
6. **Finish with a summary**: every file you changed and why; the manual steps that remain for me (Apple Team ID and bundle ID, Play signing SHA-256 fingerprint, store listing, a rebuild and reinstall) and where in the LinkForty dashboard each one goes (Workspace Settings → App Configuration); and anything you were unsure about.
7. **Verify.** Run the app once, then check the LinkForty dashboard: the SDK Doctor badge in the sidebar (https://docs.linkforty.com/guides/attribution/sdk-health) should report the SDK as installed. If the MCP server is available, create a test link with `create_link` and open it on a device or simulator; the click and the deep link should reach the screen we agreed on.
## Steps
1. **Install** with `npm install @linkforty/mobile-sdk-react-native`, then `cd ios && pod install` (https://docs.linkforty.com/sdks/react-native#installation). Android needs no extra setup (auto-linking).
2. **Initialize once at launch**, before registering any callback: `LinkForty.init({ baseUrl, appToken, debug: __DEV__ })` from `@linkforty/mobile-sdk-react-native`, with the base URL and app token from "Values" (https://docs.linkforty.com/sdks/react-native#initialize-the-sdk). Leave `apiKey` out — it is only for creating links from the app.
3. **Claim the link host** (https://docs.linkforty.com/sdks/react-native#platform-configuration): iOS — add the Associated Domains capability with `applinks:<link host>`; Android — add an `<intent-filter android:autoVerify="true">` for `https://<link host>` to the main activity in `AndroidManifest.xml`, with `android:launchMode="singleTask"`. LinkForty serves the AASA and assetlinks.json files on that host itself; nothing to upload. Tell me the Apple Team ID / bundle ID and Android package / signing SHA-256 must be entered in LinkForty under Workspace Settings → App Configuration, or the OS will not verify the domain.
4. **Deferred deep link**: register `LinkForty.onDeferredDeepLink((data) => …)` right after init. When `data` is non-null, route to the screen we agreed on using its `deepLinkPath` / `customParameters`; when it is null (organic install) do nothing.
5. **Direct deep links**: register `LinkForty.onDeepLink((url, data) => …)` and route the same way, through the app's existing React Navigation setup — do not add a second navigation system.
6. **One in-app event**: at the obvious success moment in this app (sign-up completed, purchase, or the first key action — tell me which you chose) call `LinkForty.trackEvent('signup')`, or `LinkForty.trackRevenue(amount, 'USD')` for a purchase (https://docs.linkforty.com/sdks/react-native#track-events). No personal data in properties.
## Docs
- React Native SDK: https://docs.linkforty.com/sdks/react-native (Installation, Platform Configuration, Initialize the SDK, Track Events)
- SDK integration overview: https://docs.linkforty.com/guides/attribution/sdk-integration
- Deferred deep linking: https://docs.linkforty.com/guides/attribution/deferred-deep-linking
- Link hosts and app association files: https://docs.linkforty.com/guides/links/custom-domains#your-linkforty-domain
- SDK Doctor: https://docs.linkforty.com/guides/attribution/sdk-health
## Done when
- Opening one of my links on a device without the app, installing it and launching it lands on the screen we agreed on, and the install appears under Installs in the LinkForty dashboard attributed to that link.
- Opening the same link with the app installed goes straight to that screen without a browser stop.
- The in-app event shows under Events, attached to that install.
- SDK Doctor in the dashboard sidebar reports the SDK as installed.
When you are done, tell me: exactly which files changed and why, how I can verify it worked end to end (what to tap or run, and what I should see in the LinkForty dashboard), and anything you were unsure about.It needs your app token; the prompt asks you for it, and the MCP server can supply it if you have that set up.
Features
- Deferred deep linking -- route new users to specific content after install
- Direct deep linking -- handle links when the app is already installed
- Install attribution -- match installs to the link click that drove them
- Event tracking -- log in-app events tied to attribution data
- Programmatic link creation -- create short links from your app
- Server-side URL resolution -- resolve Universal Links / App Links for enriched data
- TypeScript support -- full type definitions included
- Cross-platform -- iOS and Android with a single codebase
Requirements
- React Native >= 0.64.0
- React >= 17.0.0
- Node.js >= 20.0.0
Installation
npm install @linkforty/mobile-sdk-react-native
The SDK bundles @react-native-async-storage/async-storage and react-native-device-info as direct dependencies -- no additional peer dependency installs are needed.
iOS
After installing, run:
cd ios && pod install && cd ..
Android
No additional setup required. Auto-linking handles native module registration.
Platform Configuration
iOS -- Universal Links
- In Xcode, add the Associated Domains capability to your target
- Add your link host:
applinks:acme.toapp.mefor your LinkForty domain, andapplinks:links.example.comfor each custom domain - Your LinkForty server automatically hosts the Apple App Site Association file at
/.well-known/apple-app-site-associationon every one of those hosts
Android -- App Links
Add an intent filter to your AndroidManifest.xml:
<activity
android:name=".MainActivity"
android:launchMode="singleTask">
<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="acme.toapp.me" />
</intent-filter>
</activity>
Use your LinkForty domain as the host, and add a second <data> element for each custom domain. Your LinkForty server automatically hosts the Digital Asset Links file at /.well-known/assetlinks.json on every one of those hosts.
Quick Start
Initialize the SDK
Call init() once at app startup, before registering any callbacks:
import React, { useEffect } from 'react';
import LinkForty, { DeepLinkData } from '@linkforty/mobile-sdk-react-native';
function App() {
useEffect(() => {
LinkForty.init({
baseUrl: 'https://go.yourdomain.com',
apiKey: 'your-api-key', // optional -- required for link creation
appToken: 'at_your_app_token', // recommended for Cloud -- enables organic-install attribution
debug: __DEV__,
});
// Handle deferred deep links (new installs)
LinkForty.onDeferredDeepLink((data: DeepLinkData | null) => {
if (data) {
console.log('Attributed install:', data);
navigateToContent(data);
}
});
// Handle direct deep links (app already installed)
LinkForty.onDeepLink((url: string, data: DeepLinkData | null) => {
console.log('Deep link opened:', url);
if (data) {
navigateToContent(data);
}
});
}, []);
const navigateToContent = (data: DeepLinkData) => {
const productId = data.customParameters?.productId;
if (productId) {
// Navigate to product screen
}
};
return (/* your app */);
}
Track Events
import LinkForty from '@linkforty/mobile-sdk-react-native';
// Track a custom event
LinkForty.trackEvent('purchase', {
value: 29.99,
currency: 'USD',
productId: '123',
});
// Track a signup
LinkForty.trackEvent('signup', { method: 'email' });
// Track revenue (standardized format for dashboard aggregation)
LinkForty.trackRevenue(29.99, 'USD', { productId: '123', orderId: 'order-456' });
Create Links
import LinkForty from '@linkforty/mobile-sdk-react-native';
const result = await LinkForty.createLink({
deepLinkParameters: { route: 'PRODUCT', id: '123' },
title: 'Check out this product',
utmParameters: { source: 'app', medium: 'share' },
});
console.log(result.url); // https://go.yourdomain.com/abc123
console.log(result.shortCode); // abc123
console.log(result.linkId); // uuid
Link creation requires an API key. Pass apiKey in your init() configuration. The simplified endpoint (/api/sdk/v1/links) is only available on LinkForty Cloud, not self-hosted Core.
API Reference
init(config)
Initialize the SDK. Must be called before any other method. Idempotent -- calling it twice logs a warning and returns.
await LinkForty.init(config: LinkFortyConfig): Promise<void>
| Parameter | Type | Required | Description |
|---|---|---|---|
baseUrl | string | Yes | Your LinkForty server URL |
apiKey | string | No | API key for link creation and Cloud features |
appToken | string | No | Public workspace token. Recommended for Cloud — required for organic installs (App Store discovery, social mentions, etc.) to be attributed to your workspace. Find it in the dashboard under Workspace Settings → App Token. Safe to ship in your app bundle. |
debug | boolean | No | Enable verbose logging (default: false) |
attributionWindow | number | No | Attribution window in days (default: 7) |
autoTrackNavigation | boolean | object | No | Auto-emit screen_view events from React Navigation. Requires navigationRef. See Automatic Screen Tracking. Default: off. |
navigationRef | ref | No | Your React Navigation container ref. Required when autoTrackNavigation is enabled. |
onDeferredDeepLink(callback)
Register a callback for deferred deep links. If attribution data is already available (e.g., callback registered after init completes), the callback fires immediately.
LinkForty.onDeferredDeepLink(callback: (data: DeepLinkData | null) => void): void
The callback receives null for organic installs (no attribution match).
onDeepLink(callback)
Register a callback for direct deep links. When a LinkForty URL opens the app, the SDK resolves it server-side for enriched data, then invokes the callback.
LinkForty.onDeepLink(callback: (url: string, data: DeepLinkData | null) => void): void
trackEvent(name, properties?)
Track an in-app event. Requires a successful install report (install ID must be available).
await LinkForty.trackEvent(name: string, properties?: Record<string, any>): Promise<void>
trackRevenue(amount, currency, properties?)
Track a revenue event using the standardized format. The amount must be non-negative. All four LinkForty SDKs use the same convention so the Events dashboard can aggregate revenue.
await LinkForty.trackRevenue(amount: number, currency: string, properties?: Record<string, any>): Promise<void>
| Parameter | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Non-negative revenue amount |
currency | string | Yes | ISO 4217 currency code (e.g., USD) |
properties | Record<string, any> | No | Additional event properties |
Internally calls trackEvent('revenue', { revenue: amount, currency, ...properties }).
createLink(options)
Create a short link programmatically. Requires an API key.
await LinkForty.createLink(options: CreateLinkOptions): Promise<CreateLinkResult>
| Option | Type | Required | Description |
|---|---|---|---|
templateId | string | No | Template UUID (uses organization's default template if omitted) |
templateSlug | string | No | Template slug for URL construction |
deepLinkParameters | Record<string, string> | No | In-app routing parameters |
title | string | No | Link title |
description | string | No | Link description |
customCode | string | No | Custom short code |
utmParameters | UTMParameters | No | Campaign tracking parameters |
externalUserId | string | No | User ID for deduplication and share attribution |
Returns: { url: string, shortCode: string, linkId: string, deduplicated?: boolean }
setExternalUserId(id)
Set the external user ID for share attribution. This ID is automatically attached to all createLink() calls unless overridden per-call. Pass null to clear.
LinkForty.setExternalUserId(id: string | null): void
getExternalUserId()
Returns the current external user ID, or null if not set.
LinkForty.getExternalUserId(): string | null
getInstallId()
Get the server-assigned install UUID. Returns null if no install has been reported.
await LinkForty.getInstallId(): Promise<string | null>
getInstallData()
Retrieve cached attribution data from local storage. Returns null for organic installs.
await LinkForty.getInstallData(): Promise<DeepLinkData | null>
clearData()
Wipe all locally stored SDK data (install ID, attribution data, first-launch flag). The next app launch will be treated as a fresh install.
await LinkForty.clearData(): Promise<void>
Types
import type {
LinkFortyConfig,
DeepLinkData,
InstallAttributionResponse,
CreateLinkOptions,
CreateLinkResult,
DeferredDeepLinkCallback,
DeepLinkCallback,
} from '@linkforty/mobile-sdk-react-native';
DeepLinkData
| Field | Type | Description |
|---|---|---|
shortCode | string | The link's short code |
iosUrl | string? | iOS destination URL |
androidUrl | string? | Android destination URL |
webUrl | string? | Web fallback URL |
utmParameters | UTMParameters? | UTM tracking parameters |
customParameters | Record<string, string>? | Custom query parameters |
deepLinkPath | string? | In-app routing path (e.g., /product/123) |
appScheme | string? | App URI scheme (e.g., myapp) |
clickedAt | string? | When the link was clicked (ISO 8601) |
linkId | string? | Link UUID |
UTMParameters
| Field | Type |
|---|---|
source | string? |
medium | string? |
campaign | string? |
term | string? |
content | string? |
Automatic Screen Tracking
Enable autoTrackNavigation to emit a screen_view event on every React Navigation transition — no manual trackEvent calls per screen. Screen views flow through the normal event pipeline and carry the active deep-link attribution context, so they power App Analytics screen flow and the SDK health "events flowing" signal.
It requires a navigation container ref. Pass it as navigationRef:
import { createNavigationContainerRef, NavigationContainer } from '@react-navigation/native';
import LinkForty from '@linkforty/mobile-sdk-react-native';
export const navigationRef = createNavigationContainerRef();
LinkForty.init({
baseUrl: 'https://go.yourdomain.com',
appToken: 'at_your_app_token',
autoTrackNavigation: true, // screen names only — privacy-safe default
navigationRef,
});
// Attach the same ref to your container
<NavigationContainer ref={navigationRef}>
{/* ...screens... */}
</NavigationContainer>
Capturing route params
By default, autoTrackNavigation: true captures screen names only — never route params, since params can hold personal data. To capture specific, non-PII params, pass an options object with an explicit allow-list:
LinkForty.init({
baseUrl: 'https://go.yourdomain.com',
navigationRef,
autoTrackNavigation: {
captureParams: ['productId', 'category'], // only these keys are captured
debounceMs: 350, // collapse rapid transitions (default 350ms)
},
});
| Option | Type | Default | Description |
|---|---|---|---|
captureParams | string[] | [] (none) | Allow-list of route param keys whose primitive values may be captured. Never list keys that can hold personal data. |
debounceMs | number | 350 | Debounce window for rapid transitions, in milliseconds. |
What gets sent
Each transition emits an event named screen_view with:
| Property | Description |
|---|---|
screen | The active route name |
previousScreen | The route navigated from (when available) |
params | Allow-listed route params (only if captureParams is set) |
Rapid transitions are debounced to the final screen and consecutive duplicates are dropped. Apps that don't use React Navigation are unaffected — if autoTrackNavigation is enabled without a navigationRef, screen tracking is simply disabled (a warning is logged in debug mode).
Automatic screen tracking is available in the SDK versions that ship the navigation tracker. Check the SDK health page to confirm your installed version supports it.
Self-Hosted Configuration
If you're running LinkForty Core instead of Cloud, omit the apiKey:
await LinkForty.init({
baseUrl: 'https://links.yourcompany.com',
debug: false,
});
Link creation via createLink() without a templateId is not available on self-hosted Core -- you must provide a templateId and use the /api/links endpoint.
Troubleshooting
Deferred deep link callback not firing
- Attribution window expired -- the user installed too long after clicking the link
- Fingerprint mismatch -- different network between click and install, VPN, or iOS privacy relay
- SDK not initialized -- ensure
init()is called beforeonDeferredDeepLink() - Enable debug mode -- set
debug: trueto see detailed logs
Events not appearing in analytics
- No install ID -- events require a successful install report. Check debug logs for install errors
- Network issues -- the React Native SDK does not queue events offline (events are fire-and-forget)
iOS build errors
cd ios && pod deintegrate && pod install && cd ..
Android build errors
Ensure minSdkVersion is at least 21 in android/build.gradle.
Parity Notes
The React Native SDK has some gaps compared to the iOS, Android, and Expo SDKs:
- No offline event queue -- events are fire-and-forget; failed events are not retried
- No
reset()method -- onlyclearData()is available - No typed errors -- throws generic
Errorinstead of typed error codes - No configuration validation -- HTTPS and attribution window bounds are not enforced
See the SDK Specification for the full feature parity matrix.
Next Steps
- Create links in the dashboard
- View analytics for your app
- Configure attribution windows
- SDK Specification -- full feature reference