Appearance
Capacitor SDK
Complete guide for integrating Linkrunner in Capacitor apps
Installation
Step 1: Install the Package
bash
# Using npm
npm install capacitor-linkrunner
# Using yarn
yarn add capacitor-linkrunnerStep 2: Sync Native Projects
bash
npx cap syncStep 3: iOS Configuration
If you're developing for iOS, follow these additional steps:
- Install pods for the package:
bash
cd ios/App && pod install- Add the following code to your
Info.plistfile:
xml
<key>NSUserTrackingUsageDescription</key>
<string>This identifier will be used to deliver personalized ads and improve your app experience.</string>Step 4: SKAdNetwork Configuration
To enable SKAdNetwork postback copies to be sent to Linkrunner, add the following keys to your Info.plist file:
xml
<key>NSAdvertisingAttributionReportEndpoint</key>
<string>https://linkrunner-skan.com</string>
<key>AttributionCopyEndpoint</key>
<string>https://linkrunner-skan.com</string>For complete SKAdNetwork integration details, see the SKAdNetwork Integration Guide.
Step 5: Android Backup Configuration
For Android apps, the SDK provides backup rules to exclude Shared Preferences data from backup. This prevents the retention of the Linkrunner install ID during reinstallation, ensuring accurate detection of new installs and re-installs.
For detailed backup configuration instructions, please refer to the Android SDK Backup Configuration.
Step 6: Android Kotlin Configuration
The Capacitor Linkrunner SDK requires the Kotlin Gradle plugin. Add the following to your root build.gradle file under dependencies:
groovy
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}For example, with Kotlin version 1.9.22:
groovy
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22"
}
}Initialization (Required)
To initialize the Linkrunner SDK, add this code to your app's initialization:
You can find your project token here.
Note: The initialization method doesn't return any value. To get attribution data and deeplink information, use the getAttributionData method.
typescript
import linkrunner from "capacitor-linkrunner";
// Inside your app initialization
const init = async () => {
await linkrunner.init(
"YOUR_PROJECT_TOKEN",
"YOUR_SECRET_KEY", // Optional: Required for SDK signing
"YOUR_KEY_ID", // Optional: Required for SDK signing
false, // Optional: Set to true to disable IDFA collection for iOS devices (defaults to false)
true // Optional: Enable debug mode for development (defaults to false)
);
console.log("Linkrunner initialized");
};
// Call init when app starts
init();SDK Signing Parameters (Optional)
For enhanced security, the Linkrunner SDK accepts the following signing parameters during initialization:
secretKey: A unique secret key used for request signing and authenticationkeyId: A unique identifier for the key pair used in the signing process
You can find your project token, secret key, and key ID here.
User Identification (Required)
Call the signup method as soon as the user is identified — whether through signup or login. This is the moment Linkrunner ties the install (and any future events) to a user identifier.
It is strongly recommended to use the integrated platform's identify function to set a persistent user_id once it becomes available (typically after signup or login).
- Mixpanel - ID Management & User Identification
- PostHog - How User Identification Works
- Amplitude - Identify Users Documentation
If the platform's identifier function is not called, you must provide a user identifier for Mixpanel, PostHog, and Amplitude integration.
- mixpanel_distinct_id for Mixpanel
- posthog_distinct_id for PostHog
- amplitude_device_id for Amplitude
typescript
import linkrunner from "capacitor-linkrunner";
const onSignup = async () => {
try {
await linkrunner.signup({
user_data: {
id: "123", // Required: User ID
name: "John Doe", // Optional
phone: "9876543210", // Optional
email: "user@example.com", // Optional
// These properties are used to track reinstalls
user_created_at: "2024-01-01T00:00:00Z", // Optional
is_first_time_user: true, // Optional
mixpanel_distinct_id: "mixpanel_distinct_id", // Optional - Mixpanel Distinct ID
amplitude_device_id: "amplitude_device_id", // Optional - Amplitude User ID
posthog_distinct_id: "posthog_distinct_id", // Optional - PostHog Distinct ID
},
data: {}, // Optional: Any additional data
});
console.log("Signup successful");
} catch (error) {
console.error("Error during signup:", error);
}
};Handle Deeplink
To enable remarketing and reattribution, you need to capture deep links and pass them to the Linkrunner SDK. This allows Linkrunner to detect returning users who open the app via a deep link.
typescript
import { App } from '@capacitor/app';
import linkrunner from 'capacitor-linkrunner';
// Init SDK first
await linkrunner.init('your_project_token');
// Cold start — app was launched by a deeplink
const launchUrl = await App.getLaunchUrl();
if (launchUrl?.url) {
linkrunner.handleDeeplink(launchUrl.url);
}
// Warm start — app was in background, deeplink brought it to foreground
App.addListener('appUrlOpen', ({ url }) => {
linkrunner.handleDeeplink(url);
});Linkrunner sends the updated deeplink back after processing. For Linkrunner campaign links, use the returned deeplink as the resolved destination instead of the original tracking URL.
json
{
"deeplink": "https://app.yourdomain.com/product/123"
}Getting Attribution Data
To get attribution data and deeplink information for the current installation, use the getAttributionData function:
typescript
import linkrunner from "capacitor-linkrunner";
const getAttributionInfo = async () => {
try {
const attributionData = await linkrunner.getAttributionData();
console.log("Attribution data:", attributionData);
} catch (error) {
console.error("Error getting attribution data:", error);
}
};The getAttributionData function returns:
typescript
{
deeplink: string | null;
campaignData: {
id: string;
name: string;
type: string; // "ORGANIC" | "INORGANIC"
adNetwork: string | null; // "META" | "GOOGLE" | null
installedAt: string;
storeClickAt: string | null;
groupName: string;
assetName: string;
assetGroupName: string;
}
}Setting User Data
Call setUserData each time the app opens and the user is logged in:
setUserData is optional and is not a replacement for signup. Always call signup first as soon as the user is identified (signup or login). Use setUserData afterwards only when additional user details become available later — for example, when the user adds a phone number, email, or completes their profile after identification.
typescript
import linkrunner from "capacitor-linkrunner";
const setUserData = async () => {
await linkrunner.setUserData({
id: "123", // Required: User ID
name: "John Doe", // Optional
phone: "9876543210", // Optional
email: "user@example.com", // Optional
mixpanel_distinct_id: "mixpanel_distinct_id", // Optional - Mixpanel Distinct ID
amplitude_device_id: "amplitude_device_id", // Optional - Amplitude User ID
posthog_distinct_id: "posthog_distinct_id", // Optional - PostHog Distinct ID
});
};Setting CleverTap ID
Use the setAdditionalData method to set CleverTap ID:
typescript
import linkrunner from "capacitor-linkrunner";
const setIntegrationData = async () => {
await linkrunner.setAdditionalData({
clevertapId: "YOUR_CLEVERTAP_USER_ID", // CleverTap user identifier
});
};Parameters for linkrunner.setAdditionalData
clevertapId: string (optional) - CleverTap user identifier
This method allows you to connect user identities across different analytics and marketing platforms.
Revenue Tracking
Revenue data is only stored and displayed for attributed users. Make sure you have implemented the .signup function before capturing payments. To attribute a test user, follow the Integration Testing guide. You can verify your events are being captured on the Events Settings page.
Capture Payment
Use this method to capture payment information:
typescript
import linkrunner from "capacitor-linkrunner";
const capturePayment = async () => {
await linkrunner.capturePayment({
amount: 100, // Payment amount
userId: "user123", // User identifier
paymentId: "payment456", // Optional: Unique payment identifier
type: "FIRST_PAYMENT", // Optional: Payment type
// type: "SECOND_PAYMENT", // Optional: Payment type
status: "PAYMENT_COMPLETED", // Optional: Payment status
});
};Parameters for linkrunner.capturePayment
amount: number (required) - The payment amountuserId: string (required) - Identifier for the user making the paymentpaymentId: string (optional) - Unique identifier for the paymenttype: string (optional) - Type of payment. Available options:FIRST_PAYMENT- First payment made by the userWALLET_TOPUP- Adding funds to a walletFUNDS_WITHDRAWAL- Withdrawing fundsSUBSCRIPTION_CREATED- New subscription createdSUBSCRIPTION_RENEWED- Subscription renewalONE_TIME- One-time paymentRECURRING- Recurring paymentDEFAULT- Default type (used if not specified)
status: string (optional) - Status of the payment. Available options:PAYMENT_INITIATED- Payment has been initiatedPAYMENT_COMPLETED- Payment completed successfully (default if not specified)PAYMENT_FAILED- Payment attempt failedPAYMENT_CANCELLED- Payment was cancelled
Removing Payments
Remove payment records (for refunds or cancellations):
typescript
import linkrunner from "capacitor-linkrunner";
const removePayment = async () => {
await linkrunner.removePayment({
userId: "user123", // User identifier
paymentId: "payment456", // Optional: Unique payment identifier
});
};Parameters for linkrunner.removePayment
userId: string (optional) - Identifier for the user whose payment is being removed. If onlyuserIdis provided, all payments for that user will be removed.paymentId: string (optional) - Unique identifier for the payment to be removed
Note: At least one of paymentId or userId must be provided when calling removePayment.
Tracking Custom Events
Events are only stored and displayed for attributed users. Make sure you have implemented the .signup function before tracking events. To attribute a test user, follow the Integration Testing guide. You can verify your events are being captured on the Events Settings page. For capturing revenue, it is recommended to use the .capturePayment method instead of .trackEvent.
Track custom events in your app:
typescript
import linkrunner from "capacitor-linkrunner";
const trackEvent = async () => {
await linkrunner.trackEvent(
"purchase_initiated", // Event name
{ product_id: "12345", category: "electronics", amount: 99.99 } // Optional: Event data, include amount as a number for revenue sharing with ad networks like Google and Meta
);
};Revenue Sharing with Ad Networks
To enable revenue sharing with ad networks like Google Ads and Meta, include an amount parameter as a number in your custom event data. This allows the ad networks to optimize campaigns based on the revenue value of conversions:
typescript
const trackPurchaseEvent = async () => {
await linkrunner.trackEvent("purchase_completed", {
product_id: "12345",
category: "electronics",
amount: 149.99, // Revenue amount as a number
});
};For revenue sharing with ad networks to work properly, ensure the amount parameter is passed as a number, not as a string.
Enhanced Privacy Controls
The SDK offers options to enhance user privacy:
typescript
import linkrunner from "capacitor-linkrunner";
// Enable PII (Personally Identifiable Information) hashing
linkrunner.enablePIIHashing(true);When PII hashing is enabled, sensitive user data like name, email, and phone number are hashed using SHA-256 before being sent to Linkrunner servers.
Uninstall Tracking
Before you begin
Here's what you need to know before getting started:
Requirements:
- Capacitor SDK 1.0.1 and later
- Firebase in your Capacitor project (Android)
- Registering your app with APNs (iOS)
Android
Connect Firebase Cloud Messaging (FCM) with Linkrunner
FCM HTTP v1 API
To configure FCM HTTP V1 for uninstalls:
Enable the FCM API:
- Go to the FCM console.
- Select a project.
- Go to Project Overview > Project settings.
- Copy the Project ID. This will be required in a later step.

- Go to the Cloud Messaging tab.
- Make sure that Firebase Cloud Messaging API (V1) is set to Enabled.
Create a custom role for Linkrunner Uninstall:
- Go to the Service accounts tab.
- Click Manage service account permissions.
- A new browser tab opens in Google Cloud Platform.
- In the side menu, select Roles.
- Click + Create role.
- Enter the following details:
- Title: Linkrunner Uninstalls
- ID: lr_uninstalls
- Role launch stage: General availability
- Click + Add permissions.
- In Enter property name or value field, enter
cloudmessaging.messages.createand select it from the search results.
- Check the cloudmessaging.messages.create option and click Add.
- Click Create.
Assign Linkrunner the FCM uninstall role:
- In the side menu, select IAM.
- Open the View by Principals tab.
- Click Grant Access.
- In Add Principals -> New principals field, enter
lr-uninstalls-tracking@lr-uninstalls-tracking.iam.gserviceaccount.com - In Assign Roles -> Select a role field, enter
Linkrunner Uninstallsand select it from the search results. - Click Save.
The Linkrunner service account has been assigned the role of Linkrunner Uninstalls.
Linkrunner Dashboard
- In Linkrunner, go to Settings > Uninstall Tracking.
- Under the Android tab, enter the Firebase Project ID that you copied initially and click Save.
![]()
Integrate with Linkrunner SDK
Follow these instructions to integrate FCM with the Linkrunner SDK:
- Set up Push Notifications:
Set up push notifications in your Capacitor app. See the Capacitor Push Notifications documentation for detailed instructions.
- Configure your app to provide the device's push token to the Linkrunner SDK.
typescript
import { PushNotifications } from '@capacitor/push-notifications';
import linkrunner from 'capacitor-linkrunner';
// Request permission and get token
const initializePushNotifications = async () => {
const permStatus = await PushNotifications.requestPermissions();
if (permStatus.receive === 'granted') {
await PushNotifications.register();
}
};
// Listen for registration
PushNotifications.addListener('registration', async (token) => {
await linkrunner.setPushToken(token.value);
});
// Handle incoming push notifications
PushNotifications.addListener('pushNotificationReceived', (notification) => {
if (notification.data && notification.data['lr-uninstall-tracking']) {
// Silent notification for uninstall tracking, ignore
return;
}
// Handle other notifications here
});Custom implementations of push notification handlers can unintentionally make uninstall push notifications visible to users, disrupting the intended silent experience. To avoid this, ensure your logic checks if the notification contains lr-uninstall-tracking and handles it accordingly, as shown in the code example above.
iOS
Connect APNs with Linkrunner
Apple Developer Portal
Get the required credentials from the Apple Developer Portal:
APNs Authentication Key (p8) and Key ID:
- Go to the Apple Developer Portal.
- Select Identifiers under Certificates, IDs & Profiles.
- Click on the app you want to track uninstalls for. Then, under Capabilities, search for Push Notifications and enable it.
- Under Certificates, IDs & Profiles, select Keys and click on plus (+) icon to create a key. Enable APNs when creating the key and download the key file (p8).
- The Key ID can be found in the Keys tab.
Bundle ID and Team ID:
- Under Identifiers, click on your app and you will see the Bundle ID and Team ID (App ID Prefix).
Linkrunner Dashboard
- In Linkrunner, go to Settings > Uninstall Tracking.
- Under the iOS tab, upload the APNs Authentication Key (p8) file and enter the Key ID, Bundle ID and Team ID (App ID Prefix) that you copied from the Apple Developer Portal.
![]()
Integrate with Linkrunner SDK
Follow these instructions to integrate APNs with the Linkrunner SDK:
- Set up Push Notifications:
Set up push notifications in your Capacitor app if you haven't already. See the Capacitor Push Notifications documentation for detailed instructions.
- Configure your app to provide the device's APNs token to the Linkrunner SDK.
For iOS, the token received from the registration event is the APNs token when running on iOS devices:
typescript
import { PushNotifications } from '@capacitor/push-notifications';
import { Capacitor } from '@capacitor/core';
import linkrunner from 'capacitor-linkrunner';
// Listen for registration - on iOS this provides the APNs token
PushNotifications.addListener('registration', async (token) => {
if (Capacitor.getPlatform() === 'ios') {
await linkrunner.setPushToken(token.value);
}
});Function Placement Guide
| Function | Where to Place | When to Call |
|---|---|---|
linkrunner.init | App initialization | Once when app starts |
linkrunner.getAttributionData | Attribution data handling flow | Whenever the attribution data is needed |
linkrunner.setAdditionalData | Integration code | When third-party integration IDs are available |
linkrunner.signup | Identification flow (signup or login) | Once when the user is identified |
linkrunner.setUserData | Authentication logic | Every time app opens with logged-in user |
linkrunner.trackEvent | Throughout app | When specific user actions occur |
linkrunner.capturePayment | Payment processing | When user makes a payment |
linkrunner.removePayment | Refund flow | When payment needs to be removed |
linkrunner.setPushToken | Push notification setup | When push token is available |
linkrunner.handleDeeplink | Deep link entry points | When app is opened via a deep link |
Next Steps
Test Your Integration
Validate your setup end-to-end
Set Up Deep Linking
Configure deep links for your app
Support
If you encounter issues during integration, contact us at support@linkrunner.io.