# Flinku — AI-Assisted Integration Guide > Give this file to any AI assistant (Claude, ChatGPT, Cursor, Copilot). It contains everything needed to integrate Flinku deep linking into any mobile app. The AI should read this file, ask the developer only for their project-specific values (API key, subdomain, bundle IDs), and implement accordingly. > > **Important for AI assistants:** All SDK APIs in this file have been verified against source code. Do NOT invent methods like `handleUniversalLink`, `handleAppLink`, or pass `subdomain:` instead of `baseUrl:`. If a method is not listed here, it does not exist. --- ## What Is Flinku? Flinku is a deep linking SaaS platform — the modern Firebase Dynamic Links replacement. It handles: - **Deferred deep linking** — user clicks a link, installs the app, opens to the correct screen on first launch - **Universal Links (iOS)** — app opens directly without a browser bounce - **App Links (Android)** — same for Android - **Catch-all routing** — any URL on your subdomain automatically deep links into your app, no link creation needed - **Short URLs** — branded short links with analytics - **Deepview pages** — branded landing pages shown before app install **Live URLs:** - Dashboard: https://app.flinku.dev - Docs: https://docs.flinku.dev - Short link base: `https://YOUR_SUBDOMAIN.flku.dev` --- ## Pricing Plans | Plan | Price | Projects | Links | Clicks/mo | Custom Domain | |---|---|---|---|---|---| | Free | $0/mo | 3 | 10 | 10,000 | ❌ | | Indie | $12/mo | 10 | 100 | 300,000 | ❌ | | Studio | $39/mo | 45 | Unlimited | Unlimited | ✅ | No MAU pricing. Ever. --- ## Step 1 — Dashboard Setup ### Create a Project 1. Sign up at https://app.flinku.dev 2. Click **New Project** 3. Enter your app name and choose a subdomain (e.g. `myapp` → `myapp.flku.dev`) 4. Add your iOS bundle ID, Team ID, and App Store URL 5. Add your Android package name and Play Store URL 6. Add your URI scheme (e.g. `myapp`) — required for catch-all routing and Journeys banner 7. Save the project ### Get Your API Key Project → **Settings** tab → copy your API key (`flk_live_xxxxxxxx`) ### Get Your Project ID Project → **Settings** tab → copy the Project ID --- ## Step 2 — iOS Setup ### Associated Domains (Xcode) 1. Xcode → **Signing & Capabilities** → **+ Capability** → **Associated Domains** 2. Add: `applinks:YOUR_SUBDOMAIN.flku.dev` 3. If using a custom domain (Studio plan): also add `applinks:YOUR_CUSTOM_DOMAIN` Flinku automatically serves the AASA file — no manual hosting needed: ``` https://YOUR_SUBDOMAIN.flku.dev/.well-known/apple-app-site-association ``` ### Install iOS SDK (Swift Package Manager) ``` https://github.com/flinku-dev/ios-sdk ``` Or in `Package.swift`: ```swift .package(url: "https://github.com/flinku-dev/ios-sdk", from: "0.3.0") ``` ### iOS API — Complete Reference These are the **only** public APIs the iOS SDK exposes: ```swift // Configure (call once at app startup) Flinku.configure(baseUrl: String, apiKey: String? = nil, debug: Bool = false, timeout: TimeInterval = 5.0) // Match the device to a previously clicked link (call on every cold start) // Returns FlinkuLink (check link.matched and link.deepLink) Flinku.match() async -> FlinkuLink // True if a match has already been found this install (SDK dedupes internally) Flinku.hasMatched: Bool // Reset the matched state (for testing — clears UserDefaults) Flinku.reset() // Create a short link (requires apiKey) Flinku.createLink(_ options: FlinkuLinkOptions, completion: @escaping (Result) -> Void) Flinku.createLinks(_ options: [FlinkuLinkOptions], completion: @escaping (Result<[FlinkuCreatedLink], Error>) -> Void) ``` **There is NO `handleUniversalLink` method. There is NO `subdomain:` parameter.** Universal Links on iOS are handled natively by the OS via Associated Domains — Flinku just serves the AASA file. Don't add manual handling. ### iOS Implementation **AppDelegate.swift:** ```swift import FlinkuSDK @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { Flinku.configure( baseUrl: "https://YOUR_SUBDOMAIN.flku.dev", apiKey: "flk_live_YOUR_API_KEY" ) // Call match on every cold start — SDK dedupes internally via hasMatched Task { let link = await Flinku.match() if link.matched, let deepLink = link.deepLink { await MainActor.run { self.handleDeepLink(deepLink, params: link.params) } } } return true } func handleDeepLink(_ deepLink: String, params: [String: String]) { guard let url = URL(string: deepLink) else { return } switch url.host { case "product": let id = params["id"] ?? "" // navigate to ProductScreen(id: id) case "promo": let code = params["promo"] ?? "" // navigate to PromoScreen(code: code) case "referral": let referrerId = params["referrerId"] ?? "" // navigate to ReferralScreen(referrerId: referrerId) default: break } } } ``` **SwiftUI:** ```swift @main struct MyApp: App { init() { Flinku.configure( baseUrl: "https://YOUR_SUBDOMAIN.flku.dev", apiKey: "flk_live_YOUR_API_KEY" ) } var body: some Scene { WindowGroup { ContentView().task { let link = await Flinku.match() if link.matched, let deepLink = link.deepLink { // navigate based on deepLink + link.params } } } } } ``` ### iOS — Re-handling Universal Links When App Is Already Open (Optional) When a user is already installed and taps a Universal Link, iOS routes them to the app via `application(_:continue:restorationHandler:)`. The SDK's `match()` only returns once per install (due to `hasMatched` dedup), so subsequent taps would be missed if you only rely on it. A simple workaround: call `Flinku.reset()` then `Flinku.match()` again to re-resolve the link. ```swift func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { if userActivity.activityType == NSUserActivityTypeBrowsingWeb, userActivity.webpageURL != nil { Task { Flinku.reset() let link = await Flinku.match() if link.matched, let deepLink = link.deepLink { await MainActor.run { self.handleDeepLink(deepLink, params: link.params) } } } } return true } ``` This is **optional** — most apps that don't use Universal Links from external sources can skip this. Not doing it just means a user who's already installed and taps a Universal Link from Safari/Messages might land on the home screen instead of the deep-linked screen. --- ## Step 3 — Android Setup ### Intent Filters (AndroidManifest.xml) ```xml ``` Flinku automatically serves assetlinks.json — no manual hosting needed: ``` https://YOUR_SUBDOMAIN.flku.dev/.well-known/assetlinks.json ``` ### Install Android SDK **build.gradle (app):** ``` dependencies { implementation 'dev.flinku:android-sdk:0.3.0' } ``` ### Android API — Complete Reference ```kotlin // Configure (call once at app startup) Flinku.configure( context: Context, baseUrl: String, apiKey: String? = null, debug: Boolean = false, timeoutMs: Long = 5000L ) // Match (suspend — call from coroutine on every cold start) suspend fun Flinku.match(context: Context): FlinkuLink // Reset matched state (for testing) fun Flinku.reset(context: Context) // Create links (suspend, requires apiKey) suspend fun Flinku.createLink(options: FlinkuLinkOptions): FlinkuCreatedLink suspend fun Flinku.createLinks(links: List): List ``` **There is NO `handleAppLink` method.** App Links on Android are handled by the OS via intent filters — your `onCreate`/`onNewIntent` reads `intent.data` directly. ### Android Implementation **MainActivity.kt:** ```kotlin import dev.flinku.sdk.Flinku import kotlinx.coroutines.launch import androidx.lifecycle.lifecycleScope class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Flinku.configure( context = this, baseUrl = "https://YOUR_SUBDOMAIN.flku.dev", apiKey = "flk_live_YOUR_API_KEY" ) // Match on every cold start lifecycleScope.launch { try { val link = Flinku.match(this@MainActivity) if (link.matched && link.deepLink != null) { handleDeepLink(link.deepLink!!, link.params) } } catch (e: Exception) { // Match failures are non-fatal } } // Handle App Link if app opened via link intent?.data?.let { handleAppLinkUri(it) } } override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) intent?.data?.let { handleAppLinkUri(it) } } private fun handleAppLinkUri(uri: Uri) { // e.g. https://myapp.flku.dev/product/123 → navigate to ProductScreen(123) } private fun handleDeepLink(deepLink: String, params: Map) { when { deepLink.contains("product") -> { val id = params["id"] ?: return // navigate to ProductActivity(id) } deepLink.contains("promo") -> { val code = params["promo"] ?: return // navigate to PromoActivity(code) } deepLink.contains("referral") -> { val referrerId = params["referrerId"] ?: return // navigate to ReferralActivity(referrerId) } } } } ``` --- ## Step 4 — Flutter Setup ### Install SDK **pubspec.yaml:** ```yaml dependencies: flinku_sdk: ^0.3.2 ``` ```bash flutter pub get ``` ### Flutter API — Complete Reference ```dart // Configure (synchronous — returns void, NO await needed) Flinku.configure({ required String baseUrl, // 'https://yourapp.flku.dev' String? apiKey, // 'flk_live_xxx' (required for createLink) bool debug = false, Duration timeout = const Duration(seconds: 5), }); // Match — returns FlinkuLink? (null = no match found, NOT a matched:false object) Future Flinku.match(); // hasMatched — true if a match was already found this install bool Flinku.hasMatched; // Reset (for testing) Future Flinku.reset(); // Create links (requires apiKey) Future Flinku.createLink(FlinkuLinkOptions options); Future> Flinku.createLinks(List links); ``` ### Flutter Implementation **main.dart:** ```dart import 'package:flinku_sdk/flinku_sdk.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); // configure is synchronous — no await Flinku.configure( baseUrl: 'https://YOUR_SUBDOMAIN.flku.dev', apiKey: 'flk_live_YOUR_API_KEY', ); runApp(const MyApp()); } ``` **Splash screen — call match on every cold start:** ```dart class SplashScreen extends StatefulWidget { const SplashScreen({super.key}); @override State createState() => _SplashScreenState(); } class _SplashScreenState extends State { @override void initState() { super.initState(); _initFlinku(); } Future _initFlinku() async { try { // match() returns null when no match found final link = await Flinku.match(); if (link != null && link.deepLink != null) { if (mounted) _handleDeepLink(link.deepLink!, link.params); return; } } catch (e) { debugPrint('Flinku.match error: $e'); } if (mounted) _navigateToHome(); } void _handleDeepLink(String deepLink, Map params) { final uri = Uri.parse(deepLink); final route = uri.host; final mergedParams = {...params, ...uri.queryParameters}; switch (route) { case 'product': Navigator.pushReplacement(context, MaterialPageRoute( builder: (_) => ProductScreen(id: mergedParams['id'] ?? ''), )); break; case 'promo': Navigator.pushReplacement(context, MaterialPageRoute( builder: (_) => PromoScreen(code: mergedParams['promo'] ?? ''), )); break; case 'referral': Navigator.pushReplacement(context, MaterialPageRoute( builder: (_) => ReferralScreen(referrerId: mergedParams['referrerId'] ?? ''), )); break; default: _navigateToHome(); } } void _navigateToHome() { Navigator.pushReplacement(context, MaterialPageRoute( builder: (_) => const HomeScreen(), )); } } ``` **Creating links from Flutter (for share features):** ```dart final link = await Flinku.createLink(FlinkuLinkOptions( deepLink: 'myapp://product?id=42', params: {'ref': 'share', 'userId': currentUserId}, utmSource: 'share', )); Share.share('Check this out: ${link.shortUrl}'); ``` --- ## Step 5 — Catch-all Routing Catch-all routing lets your app generate share URLs on the fly — no API calls needed, no link creation in dashboard. Any URL on your subdomain that doesn't match an existing link is automatically handled as a deep link. ### Enable in Dashboard Project → **Settings** → scroll to **Catch-all Routing** → toggle on → Save. **Requirement:** URI scheme must be set in your project iOS and Android settings. ### How It Works When a user visits `yourapp.flku.dev/product/123`: 1. Flinku checks if there's an existing link for that path — none found 2. Constructs deep link: `yourapp://product/123` 3. Stores fingerprint for deferred deep linking 4. Serves deepview page with your app icon and branding 5. After install, `Flinku.match()` returns `yourapp://product/123` ### Generating Share URLs in Your App ```dart // Flutter — no API call needed String getShareUrl(String path) => 'https://yourapp.flku.dev/$path'; // Examples getShareUrl('product/123'); // → yourapp.flku.dev/product/123 getShareUrl('profile/ahmed'); // → yourapp.flku.dev/profile/ahmed getShareUrl('order/ABC456'); // → yourapp.flku.dev/order/ABC456 ``` ```swift // iOS / Swift func getShareUrl(_ path: String) -> String { "https://yourapp.flku.dev/\(path)" } ``` ```kotlin // Android / Kotlin fun getShareUrl(path: String) = "https://yourapp.flku.dev/$path" ``` ### Catch-all vs Manual Links | Feature | Catch-all | Manual link | |---|---|---| | Setup | One toggle | One API call per link | | Click analytics | Project-level | Per-link | | QR codes | ❌ | ✅ | | Custom OG preview | ❌ | ✅ per link | | Deferred deep linking | ✅ | ✅ | | Best for | Dynamic content (profiles, products, orders) | Campaigns, influencer links | --- ## Step 6 — React Native Setup ### Install SDK ```bash npm install flinku-react-native ``` ### React Native API — Complete Reference The RN SDK is **class-based** (not a singleton like iOS/Android/Flutter): ```typescript import { Flinku } from 'flinku-react-native'; interface FlinkuConfig { baseUrl: string; // 'https://yourapp.flku.dev' — required userId: string; // unique per device — required for fingerprinting apiKey?: string; // 'flk_live_xxx' — required for createLink timeout?: number; // ms, default 10000 } // Create instance once const flinku = new Flinku(config: FlinkuConfig); // Match — returns FlinkuLink | null (null = no match) flinku.match(): Promise // Reset flinku.reset(): Promise // Create links (requires apiKey) flinku.createLink(options): Promise flinku.createLinks(options[]): Promise ``` ### React Native Implementation ```javascript import { Flinku } from 'flinku-react-native'; import { useEffect } from 'react'; import DeviceInfo from 'react-native-device-info'; // Create the instance once (in a singleton module is best) const flinku = new Flinku({ baseUrl: 'https://YOUR_SUBDOMAIN.flku.dev', userId: DeviceInfo.getUniqueId(), apiKey: 'flk_live_YOUR_API_KEY', }); export default function App() { useEffect(() => { checkDeepLink(); }, []); async function checkDeepLink() { const link = await flinku.match(); if (link && link.deepLink) { handleDeepLink(link.deepLink, link.params); } } function handleDeepLink(deepLink, params) { const url = new URL(deepLink); switch (url.hostname) { case 'product': navigation.navigate('Product', { id: params.id }); break; case 'promo': navigation.navigate('Promo', { code: params.promo }); break; case 'referral': navigation.navigate('Referral', { referrerId: params.referrerId }); break; } } return ; } ``` --- ## Step 7 — Capacitor Setup ### Install SDK ```bash npm install flinku-capacitor npx cap sync ``` ### Capacitor API — Complete Reference Same class-based API as React Native (Capacitor SDK extends the same base class): ```typescript import { Flinku } from 'flinku-capacitor'; const flinku = new Flinku({ baseUrl: 'https://yourapp.flku.dev', userId: 'unique-device-id', apiKey: 'flk_live_xxx', }); await flinku.match(); // Promise await flinku.reset(); await flinku.createLink(options); ``` ### Capacitor Implementation ```typescript import { Flinku } from 'flinku-capacitor'; import { Device } from '@capacitor/device'; async function initFlinku() { const deviceId = (await Device.getId()).identifier; const flinku = new Flinku({ baseUrl: 'https://YOUR_SUBDOMAIN.flku.dev', userId: deviceId, apiKey: 'flk_live_YOUR_API_KEY', }); const link = await flinku.match(); if (link?.deepLink) { handleDeepLink(link.deepLink, link.params); } } ``` --- ## Step 8 — Unity Setup ### Install SDK Download from: https://github.com/flinku-dev/unity-sdk Import the `.unitypackage` into your project. ### Unity Implementation ```csharp using FlinkuSDK; public class GameManager : MonoBehaviour { async void Start() { Flinku.Configure( baseUrl: "https://YOUR_SUBDOMAIN.flku.dev", apiKey: "flk_live_YOUR_API_KEY" ); var link = await Flinku.Match(); if (link != null && link.Matched) { HandleDeepLink(link.DeepLink, link.Params); } } void HandleDeepLink(string deepLink, Dictionary parms) { if (deepLink.Contains("level")) LoadLevel(parms.GetValueOrDefault("id", "1")); else if (deepLink.Contains("invite")) HandleInvite(parms.GetValueOrDefault("referrerId", "")); } } ``` --- ## Step 9 — Journeys Web Banner Add a smart banner to your website that prompts mobile visitors to open (or install) your app. Add to your website's ``: ```html ``` - Only shows on mobile devices - Uses your app's URI scheme to open directly if installed - Falls back to App Store / Play Store if not installed - Carries context through install via deferred deep linking --- ## Step 10 — Create Links via API ### Authentication ``` Authorization: Bearer flk_live_YOUR_API_KEY ``` ### Create a Single Link ```bash POST https://flku.dev/api/links Authorization: Bearer flk_live_YOUR_API_KEY Content-Type: application/json { "projectId": "YOUR_PROJECT_ID", "title": "Summer Sale", "deepLink": "myapp://promo?promo=SUMMER", "params": { "promo": "SAVE20", "ref": "instagram" }, "utmSource": "instagram", "utmMedium": "social", "utmCampaign": "summer2026" } ``` **Response:** ```json { "id": "abc123", "slug": "summer-sale", "shortUrl": "https://myapp.flku.dev/summer-sale" } ``` ### Create Link with Advanced Options ```json { "projectId": "YOUR_PROJECT_ID", "title": "Limited Offer", "deepLink": "myapp://promo/limited", "expiresAt": "2026-06-01T00:00:00Z", "maxClicks": 1000, "password": "secret123", "scheduledAt": "2026-05-15T09:00:00Z", "geoRouting": [ { "countries": ["US", "CA"], "url": "https://us.myapp.com" }, { "countries": ["GB"], "url": "https://uk.myapp.com" } ], "ogTitle": "Limited Time Offer", "ogDescription": "Get 20% off today only", "ogImageUrl": "https://myapp.com/promo.jpg", "desktopUrl": "https://myapp.com/promo" } ``` ### Bulk Create Links (up to 500) ```bash POST https://flku.dev/api/links/bulk { "projectId": "YOUR_PROJECT_ID", "links": [ { "title": "Invite John", "deepLink": "myapp://referral", "params": { "referrerId": "john123" } }, { "title": "Invite Jane", "deepLink": "myapp://referral", "params": { "referrerId": "jane456" } } ] } ``` ### Get All Links ```bash GET https://flku.dev/api/links?projectId=YOUR_PROJECT_ID Authorization: Bearer flk_live_YOUR_API_KEY ``` ### Bulk Delete Links ```bash DELETE https://flku.dev/api/links/bulk { "ids": ["linkId1", "linkId2", "linkId3"] } ``` Deleted links go to Trash and are permanently deleted after 30 days. Users can restore from dashboard. --- ## Step 11 — Referral System ### Track a Referral ```bash POST https://flku.dev/api/referrals/track { "referrerId": "user123", "projectId": "YOUR_PROJECT_ID", "newUserId": "newuser456" } ``` Call after `Flinku.match()` if `referrerId` is present in params: ```dart final link = await Flinku.match(); if (link != null && link.params['referrerId'] != null) { await yourApi.trackReferral( referrerId: link.params['referrerId']!, newUserId: currentUserId, ); } ``` ### Get Referral Leaderboard ```bash GET https://flku.dev/api/referrals?projectId=YOUR_PROJECT_ID Authorization: Bearer flk_live_YOUR_API_KEY ``` --- ## Step 12 — Retargeting Pixel Add to your website to track visitors: ```html ``` --- ## How Deferred Deep Linking Works 1. User clicks a Flinku short link (or a catch-all URL) on their phone 2. Flinku stores a fingerprint (IP + hashed user agent) 3. User is redirected to App Store or Play Store 4. User installs and opens the app for the first time 5. App calls `Flinku.match()` on cold start 6. Flinku matches fingerprint → returns original deep link + params 7. App navigates to correct screen **Critical:** Call `Flinku.match()` on **every cold start**, not guarded by your own first-open flag. The SDK has built-in dedup — calling it repeatedly is safe and cheap. Adding your own first-open guard breaks edge cases (app reinstalls, multi-device users). --- ## Key Concepts ### baseUrl — NOT subdomain All SDKs take `baseUrl` (full URL), NOT just the subdomain. The SDK extracts the subdomain internally. ```dart // ✅ Correct Flinku.configure(baseUrl: 'https://myapp.flku.dev', apiKey: '...'); // ❌ Wrong — `subdomain:` parameter does not exist Flinku.configure(subdomain: 'myapp', apiKey: '...'); ``` ### URI Scheme Your app's custom URL scheme (e.g. `myapp`): - iOS: Add `myapp` to Info.plist under `CFBundleURLTypes` - Android: Add `android:scheme="myapp"` to intent filter - Set in Flinku project settings — required for catch-all routing ### Params Key-value pairs attached to a link, passed back via `Flinku.match()`: ``` myapp://promo?promo=SAVE20&ref=instagram&referrerId=user123 ``` ### Dynamic URL Variables Flinku supports variables in deep links that resolve at click time: - `#{ref}` — referrer URL - `#{country}` — user's country code - `#{platform}` — ios / android / web - `#{slug}` — the link slug --- ## Common Patterns ### Share Link (User-generated) ```dart // With catch-all routing — no API call needed String getProductShareUrl(String productId) => 'https://yourapp.flku.dev/product/$productId'; // Or create a tracked link via API for analytics Future createTrackedShareLink(String userId) async { final link = await Flinku.createLink(FlinkuLinkOptions( deepLink: 'myapp://referral', params: { 'referrerId': userId, 'reward': '5 USD' }, )); return link.shortUrl; } ``` ### Onboarding Flow ```dart final link = await Flinku.match(); if (link != null) { final params = link.params; if (params['promo'] != null) { navigateTo(PromoOnboardingScreen(code: params['promo']!)); } else if (params['referrerId'] != null) { navigateTo(ReferralOnboardingScreen(referrerId: params['referrerId']!)); } else { navigateTo(DefaultOnboardingScreen()); } } else { navigateTo(DefaultOnboardingScreen()); } ``` ### Campaign Links Create links in the dashboard with UTM parameters per channel: - `utmSource: "instagram"`, `utmMedium: "story"`, `utmCampaign: "launch"` - Analytics shows clicks per campaign, country, platform, browser --- ## What to Ask the Developer When integrating Flinku for a developer, collect: 1. **Subdomain** — e.g. `myapp` (used to construct `baseUrl: 'https://myapp.flku.dev'`) 2. **API key** — from Dashboard → Project → Settings (`flk_live_xxx`) 3. **Project ID** — only needed for Journeys banner script and pixel 4. **iOS bundle ID** — e.g. `com.company.myapp` 5. **iOS Team ID** — from Apple Developer account (10-char alphanumeric) 6. **Android package name** — e.g. `com.company.myapp` 7. **URI scheme** — e.g. `myapp` 8. **Deep link routes** — what screens exist and their paths (e.g. `product/:id`, `promo`, `referral`) 9. **Whether catch-all routing is needed** — for apps with dynamic content --- ## Common Mistakes to Avoid These are wrong and will cause compilation errors or runtime failures. The SDK does not provide these methods/properties: ```swift // ❌ Wrong — `subdomain:` doesn't exist Flinku.configure(subdomain: "myapp", apiKey: "...") // ✅ Correct Flinku.configure(baseUrl: "https://myapp.flku.dev", apiKey: "...") // ❌ Wrong — `handleUniversalLink` doesn't exist on iOS SDK Flinku.handleUniversalLink(url) // ✅ Correct — iOS handles Universal Links natively via Associated Domains; nothing to call // ❌ Wrong — `handleAppLink` doesn't exist on Android SDK Flinku.handleAppLink(uri) // ✅ Correct — read intent.data directly in onCreate / onNewIntent // ❌ Wrong — Flutter configure isn't async await Flinku.configure(baseUrl: '...', apiKey: '...'); // ✅ Correct — synchronous, returns void Flinku.configure(baseUrl: '...', apiKey: '...'); // ❌ Wrong — Flutter match doesn't return a `matched` field on null result if (link.matched) { ... } // throws on null // ✅ Correct — match() returns FlinkuLink? where null means no match if (link != null && link.deepLink != null) { ... } // ❌ Wrong — RN/Capacitor are class-based, not static Flinku.configure({...}); Flinku.match(); // ✅ Correct const flinku = new Flinku({ baseUrl, userId, apiKey }); await flinku.match(); ``` --- ## Troubleshooting ### Universal Links not working (iOS) - Verify AASA: `curl https://YOUR_SUBDOMAIN.flku.dev/.well-known/apple-app-site-association` - Bundle ID and Team ID must match exactly what's in Xcode - Associated Domains capability must be added - Test on a real device (not simulator) - Use the validator: https://flinku.dev/tools/aasa-validator - Don't try to call `Flinku.handleUniversalLink()` — it doesn't exist ### App Links not working (Android) - Verify assetlinks.json: `curl https://YOUR_SUBDOMAIN.flku.dev/.well-known/assetlinks.json` - SHA-256 fingerprint must match your signing key - Use the validator: https://flinku.dev/tools/android-assets-validator - Test: `adb shell am start -W -a android.intent.action.VIEW -d "https://YOUR_SUBDOMAIN.flku.dev/test"` ### Deferred deep link not matching - Make sure `Flinku.match()` is called on every cold start (not guarded by your own first-open flag) - Fingerprint window is 72 hours — if user installs more than 72h after clicking, no match - Use `Flinku.reset()` for re-testing without uninstalling ### Catch-all routing not working - Make sure URI scheme is set in project Settings (iOS and Android sections) - Enable catch-all routing toggle in project Settings → Save - Verify the path doesn't match an existing link (existing links take priority) --- ## Resources - Dashboard: https://app.flinku.dev - Docs: https://docs.flinku.dev - Flutter SDK: https://pub.dev/packages/flinku_sdk - iOS SDK: https://github.com/flinku-dev/ios-sdk - Android SDK: https://github.com/flinku-dev/android-sdk - React Native SDK: https://www.npmjs.com/package/flinku-react-native - Capacitor SDK: https://www.npmjs.com/package/flinku-capacitor - Unity SDK: https://github.com/flinku-dev/unity-sdk - Support: support@flinku.dev - AASA Validator: https://flinku.dev/tools/aasa-validator - Android Assets Validator: https://flinku.dev/tools/android-assets-validator - Deep Link Tester: https://flinku.dev/tools/deep-link-tester - UTM Builder: https://flinku.dev/tools/utm-builder