React Native SDK
The React Native SDK (@tolinku/react-native-sdk) provides event tracking, deferred deep linking, referrals, and in-app messages for React Native 0.72+.
Installation
Section titled “Installation”npm install @tolinku/react-native-sdkYou also need the peer dependency for dismiss/impression state persistence:
npm install @react-native-async-storage/async-storageIf using Expo:
npx expo install @react-native-async-storage/async-storageInitialize the SDK early in your app:
import { Tolinku } from '@tolinku/react-native-sdk';
// In App.tsx or index.jsTolinku.init({ apiKey: 'tolk_pub_your_key', // baseUrl: 'https://your-app.tolinku.com', // optional // debug: true, // optional // timeout: 30000 // optional, ms});Tolinku.configure() is the same call under the name the Android, iOS and
Flutter SDKs use. Both work; init() is what this package shipped and is not
deprecated.
User identification
Section titled “User identification”Tolinku.setUserId('user_123');
// Get current user IDconst userId = Tolinku.getUserId();
// Clear on logoutTolinku.setUserId(null);Event tracking
Section titled “Event tracking”// Simple eventawait Tolinku.track('custom.screen_view');
// Event with propertiesawait Tolinku.track('custom.purchase', { campaign: 'spring-sale', user_id: 'user_123'});
// Force flushawait Tolinku.flush();Events are batched (10 events or 5-second timer) and auto-flushed when the app enters the background via React Native’s AppState listener.
Ecommerce tracking
Section titled “Ecommerce tracking”Track purchases, cart activity, and product events via Tolinku.ecommerce:
Tolinku.setUserId('user_123');
// Track a purchaseawait Tolinku.ecommerce.purchase({ transaction_id: 'order_456', revenue: 49.99, currency: 'USD', items: [ { item_id: 'sku_1', item_name: 'T-Shirt', price: 24.99, quantity: 2 } ]});
// Track product views and cart eventsawait Tolinku.ecommerce.viewItem({ items: [{ item_id: 'sku_1' }] });await Tolinku.ecommerce.addToCart({ items: [{ item_id: 'sku_1' }] });await Tolinku.ecommerce.beginCheckout({});
// Search and ratingsawait Tolinku.ecommerce.search({ search_term: 'shoes' });await Tolinku.ecommerce.rate({ item_id: 'sku_1', rating: 4.5 });
// Force flush ecommerce eventsawait Tolinku.ecommerce.flush();Ecommerce events are batched (10 events or 5-second timer) and auto-flushed when the app enters the background. The SDK manages cart IDs automatically via AsyncStorage, clearing them after purchase.
Deep link handling
Section titled “Deep link handling”A link arrives as the URL that was tapped, exactly as it was written. That is enough while the URL is readable, but every route also has a short link, and a short link is the same route written as a code:
https://links.example.com/s7k2p9q/4821Nothing in that URL says which route it is, and nothing on the device can work it out. Short links are what the dashboard’s copy button gives you and what a QR code carries, so your app will receive them whether or not you chose to share them.
links.resolve asks Tolinku and answers with the route, the token and the
canonical path. A readable URL resolves to itself, so resolve every incoming
link rather than trying to spot the short ones. It returns nothing rather than
throwing when it cannot reach us, so your own handling stays the fallback.
import { Linking } from 'react-native';import { Tolinku } from '@tolinku/react-native-sdk';
async function handle(url: string) { const link = await Tolinku.links.resolve(url); const path = link?.deep_link_path ?? new URL(url).pathname; // path -> "/merchant/abc123", and link.token -> "abc123" navigate(path);}
Linking.addEventListener('url', ({ url }) => handle(url));Linking.getInitialURL().then(url => url && handle(url));link.token saves you working out which segment the token is, which the URL
alone does not tell you when a route’s prefix places its token mid-path.
Reading the URL without asking
Section titled “Reading the URL without asking”new URL(url).pathname is enough when you already know the shape you are
getting and would rather not wait on the network. It can only tell you what the
URL says, so it cannot expand a short code.
Deferred deep linking
Section titled “Deferred deep linking”Recover the link a user tapped before they had your app, and route them to it on first launch.
const link = await Tolinku.deferred.claimDeferredLink({ appspaceId: '64f0a1b2c3d4e5f60718',});if (link) { // Route to link.deep_link_path}Call it once, on the first launch after install. One call covers both platforms: on Android the SDK reads the Play Install Referrer itself, and everywhere else it falls back to device signals.
What it does, in order
Section titled “What it does, in order”- Play Install Referrer, on Android only. A Tolinku link sends the visitor to the store with a token attached and Play hands it back on first launch, naming the exact click rather than inferring it.
- Device signals, if there was no referrer, and always on iOS, which has no equivalent. Timezone, language, screen size and pixel ratio are matched against what the landing page recorded.
The native module for the referrer ships with this package, so there is nothing extra to install. Autolinking is per platform, so an iOS build never compiles it. In Expo Go, where no custom native module is present, the call quietly falls back to signals.
Calling it once
Section titled “Calling it once”A claim is consumed the first time it succeeds. claimDeferredLink remembers
that it asked, so calling it again costs nothing.
Only a real answer is remembered. “Nothing waiting for this device” counts. A dropped request does not, so one bad connection does not spend the install’s only chance at attribution.
The lower-level calls
Section titled “The lower-level calls”Both are still available and unchanged. They ask every time they are called and do no remembering, so use them only if you are doing that bookkeeping yourself.
const byToken = await Tolinku.deferred.claimByToken(token, appspaceId);const bySignals = await Tolinku.deferred.claimBySignals({ appspaceId });Counting taps that open your app directly
Section titled “Counting taps that open your app directly”A link that opens your app directly never reaches Tolinku, so the tap is not counted. Those taps are the ones from people who already have your app, so leaving them out makes a campaign aimed at existing customers look like it got no traffic.
trackLinkOpen reports one. Call it wherever your app receives an incoming link.
A link arrives in two places and both need it. One that launches your app cold arrives somewhere different from one tapped while the app is already running, and instrumenting only the second misses the more common case while appearing to work.
// Launched by a link, app was not running.const initial = await Linking.getInitialURL();if (initial) Tolinku.trackLinkOpen(initial);
// Tapped while the app was already open.Linking.addEventListener('url', ({ url }) => { Tolinku.trackLinkOpen(url); // your own routing});Wiring both is safe: some link plugins hand the launching link to the listener as well, and the same link inside a few seconds is reported once rather than counted twice.
Only http and https links are reported. A custom scheme means Tolinku’s own
hand-off page opened your app, and that tap was counted when the page was served.
The call never throws and never blocks.
Whether these are recorded is an Appspace setting, and it decides the bill. See Attributing app opens.
Referrals
Section titled “Referrals”const { referrals } = Tolinku;
// Create a referral codeconst result = await referrals.create({ userId: 'user_123', userName: 'Jane Doe'});
// Look up a referralconst info = await referrals.get('ABC123');
// Link a referred user (status stays pending until reward milestone is reached)await referrals.complete({ code: 'ABC123', referredUserId: 'user_456'});
// Update milestone (completes the referral if it matches the reward milestone)await referrals.milestone({ code: 'ABC123', milestone: 'first_purchase' });
// Claim reward (after granting it in your system)await referrals.claimReward('ABC123');
// Get leaderboardconst { leaderboard } = await referrals.leaderboard(10);In-app messages
Section titled “In-app messages”Use the TolinkuMessages component to display in-app messages:
import { TolinkuMessages } from '@tolinku/react-native-sdk';
function App() { return ( <View style={{ flex: 1 }}> <MainContent /> <TolinkuMessages trigger="on_open" onDismiss={(messageId) => { console.log('Dismissed:', messageId); }} onButtonPress={(action, messageId) => { // Handle CTA action URL }} /> </View> );}The component fetches messages and displays the highest-priority undismissed message as a React Native modal.
| Prop | Type | Description |
|---|---|---|
trigger | string | Filter messages by trigger type |
triggerValue | string | Filter by trigger value |
onDismiss | function | Called when the user dismisses a message |
onButtonPress | function | Called when the user taps a CTA button |
Cleanup
Section titled “Cleanup”Clean up when reconfiguring or unmounting:
await Tolinku.destroy();