Expo SDK for LinkForty — the open-source alternative to Branch.io, AppsFlyer OneLink, and Firebase Dynamic Links. Add deferred deep linking, mobile attribution, and smart link routing to your Expo app. Self-hosted, privacy-first, no per-click pricing. Pure Expo modules — no native linking required.
- Deferred deep linking (install attribution via device fingerprinting)
- Direct deep linking with server-side URL resolution
- Event tracking with offline queue (persists across app restarts)
- Revenue tracking
- Programmatic link creation
- Pure Expo modules - no native linking required (
expo-device,expo-application,expo-localization,expo-linking)
npx expo install @linkforty/mobile-sdk-expo expo-device expo-application expo-localization expo-linking @react-native-async-storage/async-storageimport LinkForty from '@linkforty/mobile-sdk-expo';
// Initialize (call once at app startup)
const response = await LinkForty.initialize({
baseUrl: 'https://go.yourdomain.com',
apiKey: 'your-api-key', // Optional, required for link creation
debug: true, // Optional, enables verbose logging
attributionWindowHours: 168, // Optional, default 7 days
});
// Check attribution
if (response.attributed) {
console.log('Install attributed!', response.deepLinkData);
}
// Listen for deferred deep links (new installs)
LinkForty.onDeferredDeepLink((data) => {
if (data) {
console.log('Deferred deep link:', data.shortCode);
// Navigate to content
}
});
// Listen for direct deep links (existing users)
LinkForty.onDeepLink((url, data) => {
console.log('Deep link received:', url);
if (data?.customParameters?.route) {
// Navigate to route
}
});await LinkForty.initialize(config); // Returns InstallAttributionResponse
LinkForty.isInitialized; // boolean getterLinkForty.onDeferredDeepLink(callback); // Deferred (install attribution)
LinkForty.onDeepLink(callback); // Direct (multiple callbacks supported)
LinkForty.handleDeepLink(url); // Manual URL pass-throughawait LinkForty.trackEvent(name, properties?);
await LinkForty.trackRevenue(amount, currency, properties?);
await LinkForty.flushEvents(); // Flush offline queue
await LinkForty.clearEventQueue(); // Clear without sending
LinkForty.queuedEventCount; // Queue size getterRequires apiKey in config.
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/tmpl/abc123await LinkForty.getInstallId(); // Cached install UUID
await LinkForty.getInstallData(); // Cached DeepLinkData
await LinkForty.isFirstLaunch(); // First launch statusawait LinkForty.clearData(); // Wipe all stored SDK data
LinkForty.reset(); // Return to uninitialized stateimport { LinkFortySDK, LinkFortyError, LinkFortyErrorCode } from '@linkforty/mobile-sdk-expo';
import type {
LinkFortyConfig,
DeepLinkData,
InstallAttributionResponse,
UTMParameters,
DeviceFingerprint,
CreateLinkOptions,
CreateLinkResult,
EventRequest,
DeferredDeepLinkCallback,
DeepLinkCallback,
} from '@linkforty/mobile-sdk-expo';All SDK errors are instances of LinkFortyError with a .code property:
| Code | When |
|---|---|
NOT_INITIALIZED |
Method called before initialize() |
ALREADY_INITIALIZED |
initialize() called twice |
INVALID_CONFIGURATION |
Bad config (HTTP on non-localhost, invalid attribution window) |
NETWORK_ERROR |
Network request failed after retries |
INVALID_RESPONSE |
Server returned non-2xx response |
DECODING_ERROR |
Failed to parse server response |
INVALID_EVENT_DATA |
Empty event name or negative revenue |
MISSING_API_KEY |
createLink() called without API key |
Events that fail to send are automatically queued in AsyncStorage (max 100 events, persists across app restarts). Successfully tracked events trigger a queue flush attempt. You can also manually flush or clear the queue.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
baseUrl |
string |
Yes | - | LinkForty server URL |
apiKey |
string |
No | - | API key for link creation |
debug |
boolean |
No | false |
Enable verbose logging |
attributionWindowHours |
number |
No | 168 |
Attribution window (1–2160 hours) |
| Platform | Package |
|---|---|
| React Native | @linkforty/mobile-sdk-react-native |
| iOS (Swift) | LinkFortySDK |
| Android (Kotlin) | LinkFortySDK |
Contributions are welcome! Please open an issue or pull request.
MIT License - see LICENSE for details.
- Documentation: docs.linkforty.com
- Issues: GitHub Issues
- Core Engine: LinkForty Core — self-hosted open-source backend
- Cloud Platform: LinkForty Cloud — hosted SaaS with dashboard, teams, and billing