GitHub Issue: Deferred Deep Link Not Triggered via Meta App Promotion Campaign
Summary
We are facing an issue where Deferred Deep Linking works perfectly when clicking the Adjust link via a mobile browser, but fails to pass the deep link data when the same link is used in a Meta App Promotion campaign.
Adjust Link Used:
https://alrighttv.go.link/m/69843434610b6e71fe97323a/1?adj_t=1xfmwvhw
Scenario 1: Manual Browser Click (Working)
- Click the link in Chrome/Safari on a device without the app.
- Redirected to Play Store.
- App installed and opened.
- User completes onboarding and payment.
Result: The app correctly receives the deep link data and automatically opens the specific show.
Scenario 2: Meta Ad Campaign (Failing)
- The same link is entered into the "Deferred Deep Link" input box in the Meta Ads Manager.
- User clicks the ad on Facebook/Instagram.
- Redirected to Play Store.
- App installed and opened.
- User completes onboarding and payment.
Result: The app does not trigger the deep link; the user lands on the default home screen instead of the specific show.
Technical Questions for Support
- Is there a specific configuration required in the Adjust dashboard to ensure Meta passes the deferred deep link parameters correctly to the SDK for a Flutter app?
- Are there known limitations with Meta's "Deferred Deep Link" field when using
go.link short links vs. raw Adjust trackers?
- Can you verify if the attribution for these Meta installs is correctly firing the
deferredDeeplinkCallback callback in the Adjust SDK?
Code Snippets
1. Adjust SDK Initialization & Deferred Deep Link Callback (adjust_service.dart)
// ================= DEFERRED DEEPLINK =================
config.deferredDeeplinkCallback = (String? deeplink) async {
if (deeplink == null) {
return;
}
try {
await HandleDeepLinkNavigationService()
.handleDeepLinkNavigationForAdjust(deeplink, campId: null);
MoEngageService.instance.trackDeepLinkReceivedEvent(
deepLinkUrl: deeplink,
source: 'deferred_adjust',
success: true,
);
} catch (e) {
MoEngageService.instance.trackDeepLinkReceivedEvent(
deepLinkUrl: deeplink,
source: 'deferred_adjust',
success: false,
errorMessage: e.toString(),
);
}
};
2. Deep Link Navigation Handler (deep_link.dart)
Future<void> handleDeepLinkNavigationForAdjust(
String deepLinkUrl, {
String? campId,
}) async {
if (_shouldSkipAsDuplicate()) {
log('🔁 Deep link already handled, skipping');
return;
}
try {
log('🔗 Parsing deep link: $deepLinkUrl');
// Resolve short Adjust links (e.g. https://alrighttv.go.link/xxxxx) to their
// full destination URL so that path segments can be parsed correctly.
final String? resolvedUrl = await Adjust.processAndResolveDeeplink(
AdjustDeeplink(deepLinkUrl),
);
log('🔗 Resolved URL: $resolvedUrl');
final normalizedUrl = normalizeDeepLink(resolvedUrl ?? deepLinkUrl);
final uri = Uri.parse(normalizedUrl);
// IMPORTANT: only pathSegments (handles // automatically)
final segments = uri.pathSegments.where((e) => e.isNotEmpty).toList();
log("uri: $uri");
log("segments: $segments");
// Find s or m dynamically
final int smIndex = segments.indexWhere((e) => e == 's' || e == 'm');
late final String type;
late final String movieId;
late final int episodeIndex;
int? seasonNumber;
String seasonId = '';
String configId = '';
String trialConfigId = '';
if (smIndex == 0) {
// Normal case: s/m exists
type = segments[smIndex];
movieId = segments[smIndex + 1];
var nextSegmentIndex = smIndex + 2;
if (segments.length > nextSegmentIndex &&
int.tryParse(segments[nextSegmentIndex]) == null) {
seasonId = segments[nextSegmentIndex];
nextSegmentIndex++;
}
episodeIndex = segments.length > nextSegmentIndex
? int.tryParse(segments[nextSegmentIndex]) ?? 0
: 0;
// ... (season, configId, trialConfigId parsing)
} else {
// Adjust / redirect stripped s/m prefix
type = 'm'; // default
movieId = segments[0];
var nextSegmentIndex = 1;
if (segments.length > nextSegmentIndex &&
int.tryParse(segments[nextSegmentIndex]) == null) {
seasonId = segments[nextSegmentIndex];
nextSegmentIndex++;
}
episodeIndex = segments.length > nextSegmentIndex
? int.tryParse(segments[nextSegmentIndex]) ?? 0
: 0;
// ... (season, configId, trialConfigId parsing)
}
final campaignId = uri.queryParameters['adj_t'];
await addCampaignIdToServer(campaignId ?? 'sharing');
// Store deep link data
box.qWrite(StorageVariable.deepLinkMovieId, movieId);
box.qWrite(StorageVariable.deepLinkEpisodeIndex, episodeIndex);
box.qWrite(StorageVariable.isFromDeepLink, true);
final String? token = box.read(StorageVariable.token);
final bool isPremiumPlan = box.read(StorageVariable.isPremiumPlan) ?? false;
final bool freeTrial = box.read(StorageVariable.freeTrial) == true;
if (token == null || token.isEmpty) {
log('🔐 User not logged in. Deep link stored.');
return;
}
if (Get.context != null) {
if (isPremiumPlan || freeTrial) {
launchScreen(
Get.context!,
VideoPreviewScreen(
movieId: movieId,
episodeIndex: episodeIndex,
seasonNumber: seasonNumber,
seasonId: seasonId.isNotEmpty ? seasonId : null,
),
isNewTask: true,
pageRouteAnimation: PageRouteAnimation.Slide,
);
} else {
launchScreen(
Get.context!,
SubscriptionModelView(fullView: true),
isNewTask: true,
arguments: {
'configId': configId,
'trialConfigId': trialConfigId,
},
pageRouteAnimation: PageRouteAnimation.SlideBottomTop,
);
}
}
} catch (e) {
log('❌ Error handling deep link: $e');
MoEngageService.instance.trackDeepLinkReceivedEvent(
deepLinkUrl: deepLinkUrl,
source: 'adjust',
success: false,
errorMessage: e.toString(),
);
}
}
3. Android Build Configuration (android/app/build.gradle)
plugins {
id "com.android.application"
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
android {
namespace = "com.app.alright"
compileSdk = 36
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
coreLibraryDesugaringEnabled true
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
defaultConfig {
applicationId = "com.app.alright"
minSdkVersion 24
targetSdk = 35
versionCode = flutter.versionCode
versionName = flutter.versionName
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
buildFeatures {
buildConfig true
}
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
implementation 'com.android.billingclient:billing:6.1.0'
implementation 'androidx.browser:browser:1.2.0'
// Adjust SDK
implementation 'com.adjust.sdk:adjust-android:4.38.0'
// Google Play services for Adjust
implementation 'com.google.android.gms:play-services-ads-identifier:18.1.0'
implementation 'com.android.installreferrer:installreferrer:2.2'
implementation("androidx.core:core:1.6.0")
implementation("androidx.appcompat:appcompat:1.3.1")
implementation("androidx.lifecycle:lifecycle-process:2.7.0")
// MoEngage SDK
implementation("com.moengage:moe-android-sdk:14.02.00")
implementation("com.moengage:rich-notification:6.1.0")
implementation("com.moengage:inapp:9.1.1")
implementation("com.github.bumptech.glide:glide:4.16.0")
implementation("com.razorpay:checkout:1.6.41")
implementation("com.razorpay:standard-core:1.7.7")
implementation("com.razorpay:core:1.0.6")
implementation 'com.mux:stats.muxcore:8.8.0'
}
flutter {
source = "../.."
}
GitHub Issue: Deferred Deep Link Not Triggered via Meta App Promotion Campaign
Summary
We are facing an issue where Deferred Deep Linking works perfectly when clicking the Adjust link via a mobile browser, but fails to pass the deep link data when the same link is used in a Meta App Promotion campaign.
Adjust Link Used:
https://alrighttv.go.link/m/69843434610b6e71fe97323a/1?adj_t=1xfmwvhwScenario 1: Manual Browser Click (Working)
Result: The app correctly receives the deep link data and automatically opens the specific show.
Scenario 2: Meta Ad Campaign (Failing)
Result: The app does not trigger the deep link; the user lands on the default home screen instead of the specific show.
Technical Questions for Support
go.linkshort links vs. raw Adjust trackers?deferredDeeplinkCallbackcallback in the Adjust SDK?Code Snippets
1. Adjust SDK Initialization & Deferred Deep Link Callback (
adjust_service.dart)2. Deep Link Navigation Handler (
deep_link.dart)3. Android Build Configuration (
android/app/build.gradle)plugins { id "com.android.application" id 'com.google.gms.google-services' id 'com.google.firebase.crashlytics' id "kotlin-android" id "dev.flutter.flutter-gradle-plugin" } android { namespace = "com.app.alright" compileSdk = 36 ndkVersion = flutter.ndkVersion compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 coreLibraryDesugaringEnabled true } kotlinOptions { jvmTarget = JavaVersion.VERSION_17.toString() } defaultConfig { applicationId = "com.app.alright" minSdkVersion 24 targetSdk = 35 versionCode = flutter.versionCode versionName = flutter.versionName } signingConfigs { release { keyAlias keystoreProperties['keyAlias'] keyPassword keystoreProperties['keyPassword'] storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null storePassword keystoreProperties['storePassword'] } } buildTypes { release { minifyEnabled false shrinkResources false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' signingConfig signingConfigs.release } } buildFeatures { buildConfig true } } dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4' implementation 'com.android.billingclient:billing:6.1.0' implementation 'androidx.browser:browser:1.2.0' // Adjust SDK implementation 'com.adjust.sdk:adjust-android:4.38.0' // Google Play services for Adjust implementation 'com.google.android.gms:play-services-ads-identifier:18.1.0' implementation 'com.android.installreferrer:installreferrer:2.2' implementation("androidx.core:core:1.6.0") implementation("androidx.appcompat:appcompat:1.3.1") implementation("androidx.lifecycle:lifecycle-process:2.7.0") // MoEngage SDK implementation("com.moengage:moe-android-sdk:14.02.00") implementation("com.moengage:rich-notification:6.1.0") implementation("com.moengage:inapp:9.1.1") implementation("com.github.bumptech.glide:glide:4.16.0") implementation("com.razorpay:checkout:1.6.41") implementation("com.razorpay:standard-core:1.7.7") implementation("com.razorpay:core:1.0.6") implementation 'com.mux:stats.muxcore:8.8.0' } flutter { source = "../.." }