Introduction
AppsFlyer allows you to access the user attribution data in real time directly at the SDK level. It enables you to customize the landing page a user sees on the very first app open after a fresh app install. This is commonly referred to as “deferred” deep linking. This is very common on the web, however there is a big challenge doing this in the mobile app ecosystem. Luckily, AppsFlyer provides support for all cases and platforms.
Sending a new app user to a deep link on install is very similar to using app deep linking custom URL Scheme (iOS, Android) for users that have already installed your app.
Example
Consider a user who has just clicked on a HotelTonight’s Google Adwords ad for a "Hotels New York" search. The user is first sent to Google Play/App-Store to download the app and upon first app open the user lands directly on the Hotels in New York page. By accessing the attribution data provided by AppsFlyer, the application will receive the exact campaign and keywords used to drive that user/install.
Besides the enhanced experience and improved conversion, this functionally enables sophisticated context based campaigns such as providing the user with a credit/bonus for installing and using the app. For example: Instead of Click to install campaigns to Install and get $50 credit for booking. These campaigns not only improve the click to install conversion, but also improve the conversion to paying users. These campaigns can improve overall ROI by 2X-5X!
AppsFlyer's NativeTrack™ SDK is performing server side tracking. That means that the conversion track is processed and available on AppsFlyer’s servers. Accessing the attribution data from AppsFlyer's SDK might take few seconds from the first app open.
Note
According to Facebook's privacy policy, AppsFlyer (or any other 3rd party mobile measurement partner) cannot provide user-level attribution for Facebook installs unless you accept Facebook's Terms of Service. More info here.
If you choose not to accept the Terms of Service, Facebook Mobile Ads installs are categorized as ‘Organic’ and you are unable to receive the user level data for Facebook installs.
Accessing the attribution data to implement deferred deep linking is applicable for the user's first launch after the install. To implement deep linking for users who already have the app installed, see here.
SDK Implementation
Note
Supported by AF Android SDK 2.3.1.6 and AF iOS SDK 2.5.3.8 and above.
OnInstallConversionDataLoaded
has the attribution data of the install. You can use this function to:
- Deferred Deep linking - customize your app’s landing page to user that had entered the app for the first time.
- Get install attribution data for different events during the user's lifetime within your app
Example
A user downloads and launches your app for the first time after seeing your ad for red shoes on Facebook. To send the user directly to the red shoes page in your app, use OnInstallConversionDataLoaded
To access AppsFlyer's conversion data from the Android SDK implement the ConversionDataListener:
public interface AppsFlyerConversionListener {
void onInstallConversionDataLoaded(Map<String,String> conversionData);
void onInstallConversionFailure(String errorMessage);
}
The delegate below is used if you want to access AppsFlyer's conversion data from the SDK.
(void) onConversionDataReceived:(NSDictionary*) installData;
onConversionDataReceived
has the attribution data of the install. You can use this function to:
- Deferred Deep Linking - customize your app’s landing page for a user who is opening the app for the first time.
- Get install attribution data for different events during the user's lifetime within your app.
Example
A user downloads and launches your app for the first time after seeing your ad for red shoes on Facebook. To send the user directly to the red shoes page in your app, use onConversionDataReceived
To access AppsFlyer's conversion data from the iOS SDK implement the AppsFlyerTrackerDelegate protocol:
@protocol AppsFlyerTrackerDelegate <NSObject>
@optional
- (void) onConversionDataRequestFailure:(NSError *)error;
@end
Code Samples
Set out below are different code samples for Android and iOS.
AppsFlyerLib.getInstance(). registerConversionListener(this, new AppsFlyerConversionListener() {
@Override
public void onInstallConversionDataLoaded( Map<String, String> conversionData) {
for (String attrName : conversionData.keySet()) {
Log.d(AppsFlyerLib.LOG_TAG, "attribute: " + attrName + " = " +
conversionData.get(attrName));
}
}
@Override
public void onInstallConversionFailure( String error) {
Log.d(AppsFlyerLib.LOG_TAG, "error getting conversion data: " + error);
}
});
#import "AppsFlyerTracker.h"
@interface AppDelegate : UIResponder<UIApplicationDelegate, AppsFlyerTrackerDelegate> {
...
}
- (BOOL)application:(UIApplication ?*)application didFinishLaunchingWithOptions:(NSDictionary*?)launchOptions {
[AppsFlyerTracker sharedTracker].appsFlyerDevKey = @"[MY_DEV_KEY]";
[AppsFlyerTracker sharedTracker].appleAppID = @"123456789";
-(void)onConversionDataReceived:(NSDictionary*) installData {
id status = [installData objectForKey:@"af_status"];
if([status isEqualToString:@"Non-organic"]) {
id sourceID = [installData objectForKey:@"media_source"];
id campaign = [installData objectForKey:@"campaign"];
NSLog(@"This is a none organic install. Media source: %@ Campaign: %@",sourceID,campaign);
} else if([status isEqualToString:@"Organic"]) {
NSLog(@"This is an organic install.");
}
}
-(void)onConversionDataRequestFailure:(NSError *) error {
NSLog(@"%@",error);
}
class AppDelegate: UIResponder, UIApplicationDelegate, AppsFlyerTrackerDelegate{
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
AppsFlyerTracker.shared().appsFlyerDevKey = "MY_DEV_KEY"
AppsFlyerTracker.shared().appleAppID = "123456789"
AppsFlyerTracker.shared().delegate = self
//AppsFlyerTracker.shared().isDebug = true
//AppsFlyerTracker.shared().appInviteOneLinkID = "ONELINK_ID";
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
AppsFlyerTracker.shared().trackAppLaunch()
}
func onConversionDataReceived(_ installData: [AnyHashable : Any]!) {
if let data = installData{
print("\(data)")
if let status = data["af_status"] as? String{
if(status == "Non-organic"){
if let sourceID = data["media_source"] , let campaign = data["campaign"]{
print("This is a Non-Organic install. Media source: \(sourceID) Campaign: \(campaign)")
}
} else {
print("This is an organic install.")
}
}
}
}
func onConversionDataRequestFailure(_ error: Error!) {
if let err = error{
print(err)
}
}
func onAppOpenAttribution(_ attributionData: [AnyHashable : Any]!) {
if let data = attributionData{
print("\(data)")
}
}
func onAppOpenAttributionFailure(_ error: Error!) {
if let err = error{
print(err)
}
}
{
"af_status": "Non-organic",
"media_source": "tapjoy_int",
"campaign": "July4-Campaign",
"agency": "starcomm",
"af_siteid": null,
"af_sub1": "subtext1",
"af_sub2": null,
"af_sub3": null,
"af_sub4": null,
"af_sub5": null,
"freehand-param": "somevalue",
"click_time": "2014-05-23 20:11:31",
"install_time": "2014-05-23 20:12:16.751"
}
First Launch
The conversion data is sent from AppsFlyer's servers in real time to the newly installed app upon first launch. The SDK then stores this data in the app's cache memory (shared preference or userDefault).
With any following launch of the app the AppsFlyer's SDK reads the conversion data stored in the cache and doesn't query AppsFlyer's servers. Therefore, the conversion data ALWAYS returns the same values, whether on first launch or not.
To help developers use the conversion data on first launch only, AppsFlyer has added a parameter called is_first_launch
in the conversion data response. is_first_launch
is true on the app's first launch, and false afterwards. It is available on both Android (from SDK version 4.8.4) and iOS SDKs (from SDK version 4.8.2).
If you want to perform deferred deep linking on first launch only, do so after checking if is_first_launch
is true.
Note
Since the conversion data is stored in the shared memory of the app, users may delete it if they clean the app's data. This causes is_first_launch
to become true again. Therefore, following launches after app's data deletion are handled as first installs.
Available Keys in Attribution Response
The returned conversion data consists of ALL the parameters on the original tracking link and some additional server parameters created on the time of click or install.
Since the conversion data is reliant on the tracking link, it means that different sources and tracking links may produce different conversion data parameters.
The following function is called every time the app is launched:
onInstallConversionDataLoaded
for Android
onConversionDataReceived
for iOS
There are 3 possible outcomes depending on the type of the install:
- Non-Organic Installs
- Returns the original attribution data of the install (see the examples below)..
- Organic Install
- (or re-install) Returns "organic install"
- Re-attribution
- Returns the re-attribution conversion details.
Key Name Explanations
Key Name | Explanation | Example Values | Media Sources |
---|---|---|---|
af_status |
Shows what type of attribution was detected. Valid values:
|
Non-organic |
All |
af_message |
Free text |
Organic install/Error message |
All |
media_source |
Media source name. This is the AF tracking link ‘pid’ parameter |
NoteAgency derived install sources are hidden and have "null" value. |
All
|
campaign |
Campaign name (AppsFlyer’s tracking link ‘c’ parameter or Facebook campaign name) |
Ad1/camp123 |
All |
clickid |
Click id or transaction id |
123456/xsfd234 |
All |
af_siteid |
Site id (for optimization) |
Site1 |
All |
af_sub1 |
Extra parameter |
someParameter |
All |
af_sub2 |
Extra parameter |
|
All |
af_sub3 |
Extra parameter |
|
All |
af_sub4 |
Extra parameter |
|
All |
af_sub5 |
Extra parameter |
|
All |
af_keywords |
Keywords searched for in search campaigns. E.g. Google Search Campaigns |
All |
|
click_time |
Click date & time (milliseconds) |
2014-01-08 00:07:53.233 |
All |
install_time |
Conversion date & time (milliseconds) |
2014-01-08 00:12:51.701 |
All |
agency |
Agency or PMD generating the install |
nanigans |
All |
is_first_launch |
true for the first launch and false afterwards |
true |
All |
is_fb
|
Flag indicating it’s a Facebook attribution. Values: true/false |
true |
|
ad_id |
Facebook’s unique identification number of an ad |
6012740800279 |
|
campaign_id |
Facebook’s campaign ID |
6012700005123 |
|
adset |
Facebook’s ad set name |
US - 18+ |
|
adset_id |
Facebook ad set ID |
6099800005123 |
|
Note
For Tracking Link integrations (networks that are not self-reporting) all URL parameters appearing in the tracking link are returned in the GCD.
Regular AppsFlyer Tracking Link Install Example
An install generated using the following tracking link:
http://app.appsflyer.com/com.greatapp?pid=network_int&c=network_TH&af_sub1=102619&af_sub2=network_TH_G001_Android&af_dp=app%3A%2F%2Fhome&af_prt=expertagency&
af_siteid=1777215&af_sub_siteid=1702&freehand-param=somevalue&tag={TAGID}&clickid={CLICKID}&af_click_lookback=1d
http://app.appsflyer.com/id123456789?pid=network_int&c=network_KR&af_sub1=CD48704_&af_sub2=network_KR_G001_iOS&af_dp=app%3A%2F%2Fhome&af_prt=expertagency&
af_siteid=1777236&af_sub_siteid=1702&freehand-param=somevalue&tag={TAGID}&clickid={CLICKID}&af_click_lookback=1d
produces the following
Conversion Data Response
{
"media_source":"network_int",
"campaign":"network_TH",
"adset":null,
"clickid":"fb7f51d42-2621-93bd-e9a1b24f1acfab7b76e5104706104f4d6*******",
"adgroup":null,
"campaign_id":null,
"af_cost_currency":"USD",
"af_status":"Non-organic",
"af_sub_siteid":"1702",
"agency":"expertagency",
"af_sub3":null,
"af_cost_model":"CPI",
"af_siteid":"1777215",
"af_dp":"app://home",
"adset_id":null,
"click_time":"2017-07-19 08:30:31.890",
"cost_cents_USD":"150",
"iscache":true,
"is_first_launch":true,
"af_cpi":null,
"af_sub1":"102619",
"af_cost_value":"1.5",
"af_click_lookback":"1d",
"af_sub4":null,
"site_id":"1777215",
"adgroup_id":null,
"tag":"8d55089f-31b6-407b-9266-*********",
"orig_cost":"1.5",
"af_prt":"expertagency",
"af_sub5":null,
"install_time":"2017-07-19 08:30:35.461",
"af_sub2":"network_TH_G001_Android",
"freehand-param":"somevalue"
}
{
"media_source":"network_int",
"campaign":"network_KR",
"adset":null,
"adgroup":null,
"campaign_id":null,
"af_cost_currency":"USD",
"af_status":"Non-organic",
"agency":"expertagency",
"af_sub3":null,
"af_cost_model":"CPI",
"af_siteid":"1777236",
"af_dp":"app://home",
"adset_id":null,
"click_time":"2017-07-18 14:48:42.896",
"cost_cents_USD":"0",
"iscache":true,
"af_cpi":null,
"af_sub1":"CD48704_",
"af_click_lookback":"1d",
"af_sub4":null,
"site_id":"1777236",
"adgroup_id":null,
"tag":"43fafd60-76ad-4a8f-9d1d-************",
"orig_cost":"0.0",
"af_prt":"expertagency",
"af_sub5":null,
"install_time":"2017-07-18 15:09:06.014",
"af_sub2":"network_KR_G001_iOS",
"clickID":"3gggBgAw2Bvxa8gR56ZA8Y3qjUy2gPkFgP6rA96s4e*******",
"freehand-param":"somevalue"
}
Note
The order of the keys may vary. Additional keys might be added without notice.
Facebook Install Example
Note
There is no tracking link for Facebook campaigns.
Deep Link parameters defined in Facebook campaigns are not available outside of Facebook. This includes the af_dp parameter, which holds the scheme path in the app. Therefore, to implement deferred deep linking, additional logic must be applied for Facebook campaigns. Use the Facebook data in getConversionData
response, such as, campaign, adset, adgroup etc. to programmatically redirect your users.
Conversion Data Response
{
"adset":"T:DAT-Desktop_O:All_L:AR-AE_A:All_R:1-30 Day",
"adgroup":"T:DAT-Desktop_O:All_L:AR-AE_A:All_R:1-30 Day",
"campaign_id":"6068535534218",
"af_status":"Non-organic",
"agency":null,
"af_sub3":null,
"af_siteid":null,
"adset_id":"6073532011618",
"is_fb":true,
"is_first_launch":true,
"click_time":"2017-07-18 12:55:05",
"iscache":false,
"ad_id":"6074245540018",
"af_sub1":null,
"campaign":"T:DAT_L:AR-AE",
"is_paid":true,
"af_sub4":null,
"adgroup_id":"6073532011418",
"is_mobile_data_terms_signed":true,
"af_channel":"Facebook",
"af_sub5":null,
"media_source":"Facebook Ads",
"install_time":"2017-07-19 08:06:56.189",
"af_sub2":null
}
{
"media_source":"Facebook Ads",
"campaign":"T:App Install_A:ALL",
"adset":"T:App Install_M:iOS_O:ALL_L:DE-DE_A:ALL",
"adgroup":"T:App Install_M:iOS_O:ALL_L:DE-DE_A:ALL_Banne",
"campaign_id":"6074766693717",
"af_status":"Non-organic",
"agency":null,
"af_sub3":null,
"af_siteid":null,
"adset_id":"6074767207317",
"is_fb":true,
"click_time":"2017-07-17 16:23:18",
"iscache":false,
"ad_id":"6078076656717",
"af_sub1":null,
"is_paid":true,
"af_sub4":null,
"adgroup_id":"6074821181517",
"is_mobile_data_terms_signed":true,
"af_channel":"Facebook",
"af_sub5":null,
"install_time":"2017-07-18 15:10:50.190",
"af_sub2":null
}