Everything a developer needs to know about deep linking

Deep linking developers guide

The state of attribution and deep linking today

Most people don’t think about attribution and deep linking in the right way today.

Legacy tools like Kochava and Tune that have fallen behind on attribution, and have given the process and technical nuances a bad rap.

Newcomers only think about UX and neglect the technical intricacies of attribution, API accessibility, partnership integrations, privacy, security, and robust technical support in both integration and tool usage.

Most infuriating, sales teams across the ecosystem confuse marketers by spinning tales about how attribution and deep linking are related when it suits them, and not related when it doesn’t.

The reality is attribution and deep linking go hand in hand – you don’t need two vendors to do the same thing.

World-class attribution with all the major providers are table stakes. If you can’t attribute your campaigns effectively across every known touchpoint, it doesn’t matter whether you can take a user to a specific spot in the app.

As a technical product manager, marketer, or engineer, your focus should not be on ephemeral sales pitches, touting things like “optimized user flow” or “flawless user experiences.”

It should be on creating a world-class technical implementation, knowing that compelling UX starts and stops with proper tool setup and usage. In today’s world, compelling UX means handling every OS, OS version, app version, browser and transition experience. There are millions of edge cases which all contribute to the collective experience.

The purpose of this guide is to help a tech-savvy audience get it right when it comes to attribution and deep linking and to learn and leverage some of the most advanced topics surrounding the space.

Connection between attribution and deep linking
Chapter 1

Setting the stage: Attribution and deep linking

Let’s set the stage for this playbook by first reminding ourselves of some fundamentals regarding attribution and deep linking.

What attribution is and why it exists

The app stores do not provide a reliable way to pass data about a user from the time they enter the app to the time they install and open.

In order to “capture” their channel and other important data about a user’s originating source, link the campaign that prompted them to download the app, you must use 3rd party vendors to attribute users.

This is attribution.

The vendors who supply such technology are attribution providers.

A vendor SDK is able to attribute a user generally identifying the device on mobile web and then matching the user when they open the app. For example, when you click an AppsFlyer URL, the link redirects you briefly to a page that takes a snapshot of your device.

It is then that the vendor points you to the appropriate app store to download the app.

There is no data passage through the app store – except on Android, with the Play Store Receiver. So, the vendor’s SDK inside the app is downloaded when the user installs. And when this user opens and launches the app, the SDK takes an analogous snapshot of the user and sends it to their system to “match” the user.

Why does attribution even matter?

There’s loads of reasons, but here are a few that should convince anyone that asks:

  1. You cannot trust the paid media channels to accurately report your data. They have an inherent conflict of interest in reporting accurate data to you.
  2. You need a single unbiased vendor to count data across sources and pipe this to the Data Aggregator.
  3. Deep linking users from your paid attribution sources drives a 31% retention to the app.
  4. Users convert at a 2.5x higher rate when they come from a deep link compared to a regular attribution link.
  5. Deep links have higher intent, which means a much higher lift in revenue. In fact, industry reports show that deep linking provides a 148% lift in average revenue per user (ARPU).

Deep linking is an extension of attribution

A common misconception that exists in the market is that attribution and deep linking are somehow separate ideas or technologies.

This is simply not true.

Deep linking is the extension or usage of attribution data.

Let’s go through a basic example of how it works to illuminate the tech. In the explanation previously, we talked about how a user may click a link or an ad:

  1. The attribution vendor then points the user to the appropriate app store to download the app.

  2. When this user opens the app either after install or from the click, the attribution SDK takes an analogous snapshot of the user and sends it to their system to “match” the user.

    They also tie this to an IDFA on iOS and and GAID on Android to persistently record the user. Matching can be done with a variety of methods, such as the ability to receive and match the Google Play Store Referrer, the iOS Safari View Controller Cookie, among other methods.

  3. When the user has the app, the match is deterministic in nature because the app opens immediately via deterministic flows, such as URI Schemes (my-app://), Universal Links, Android App Links, and Chrome Intents (activity registered and handled in the manifest).

    In these latter cases, the app is getting the link the user came from, and so a tool like AppsFlyer could append a deterministic key value pair query parameter set that could be seen in the click and immediately retrieved from the SDK upon app open.
ID matching flow - attribution
Probabilistic modeling flow - attribution
Referrer flow - attribution

At this moment in time, then the SDK provides a callback to the app that contains the link and other association attribution metadata.

Below is a sample of the AppsFlyer SDK callback provided on iOS.

campaign: “CAMPAIGN_NAME”, // “None” if not specified on the link
media_source: “MEDIA_SOURCE”, //”None” if not specified on the link
cost_cents_USD : “0”,
is_first_launch: true,
install_time: “2018-12-30 23:59:39.330”,
orig_cost: “0.0”,
af_click_lookback: “7d”,
click_time: “2018-12-30 23:59:09”,
cost_cents_USD: “0”,
af_status: “Non-organic”

Now, when you get this callback data, you can do anything you want – log a custom event, fire a message, log some activity, etc. But most importantly, you can look at the attribution data and take the user to a particular spot in your app. This is deep linking.

Deferred deep linking is just the process of using data the first time the app opens after a user installs to retrieve the attribution data, parse the link or defined path and route the user to that page in the app.

Debunking attribution myths

In the methodology described, an attribution partner provides attribution data based on matched devices and deterministic known devices between web and app.

First, let’s talk about deterministic matching and probabilistic modeling.

Deterministic matching is when a user can be attributed and deep linked with 100% accuracy.

This is grounded in technology such as URI scheme launches, Android chrome intents, Android play store receiver callbacks, Android App Links, iOS Universal Links.

In all these cases the app opens from a click and a deterministic indicator is supplied with the app open to ensure 100% accuracy.

Let’s take an example: spotify://track/123?click_id=known_user_ABCD123

In this case, when a link is clicked in a browser that supports URI scheme launches, such as Chrome on iOS, and the Android operating platform, the app will be launched from a redirect through an attribution link. What’s happening is that the attribution link redirecting the user from the browser to the app with javascript (think… window.location.href…).

This means the app will launch and an identifier for the upstream click can be supplied to the app. The SDK inside the app can parse this value and match the user instantly with 100% accuracy. This same system and methodology works for the technologies above, like Android App Links and Apple Universal Links.

This is deterministic matching and it’s 100% accurate.

Probabilistic modeling is where a user is matched from a click using statistical likelihood.

After the user installs and opens the app the SDK will receive a callback from the server indicating the source of attribution.

This method is considered probabilistic, because the attribution vendor is matching based on a set of highly predictable but sometimes conflicting parameters.

While the chances of an exact probabilistic mismatch are low, it still occurs in the real world and the reason why technical experts leveraging deep linking for UX flows that might contain highly personal information need to be well informed about the differences between attribution methodologies.

Now that we have discussed these two different types of matching, let’s talk about the issue at stake: Sometimes vendors will claim to have a “higher match rate” or discuss “people-based attribution methods” that claim they are able to better attribute users across channels.

There is some amount of truth to this, but it’s important to unpack the assumptions and understand why and how.

Now let’s discuss “higher match rates”

First, higher match rates are a function of how many devices and people have been matched via a probabilistic model.

This in turn is a proxy of:

  1. The types of companies that use the attribution technology
  2. How they use that technology
  3. How anonymous attribution data is shared and leveraged

For example, AppsFlyer has more than 85,000 apps using the AppsFlyer SDK – the most of any attribution player in the space.

More importantly, however, is that AppsFlyer has brands with massive global reach using their SDK to create structured links across a variety of channels. Walmart, Alibaba, eBay, Epic Games (Fortnite), Microsoft (Minecraft),, Coca-Cola, Waze, and HBO are going to have more reach in terms of end users.

The second criteria for measuring accuracy is by far the most important.

Companies misconstrue terms like “people-based attribution is more efficient.”

What they really mean is that if you can capture a user from multiple touchpoints – across web and app – then your ability to match and attribute that user will inherently be better. If you use attribution links across paid media, email, smart banner, referral, then you will inherently be recording people better across more touchpoints and therefore your ability to properly attribute them will be better!

And third, how a company chooses to share or not share user data is important.

In 2018 we watched as GDPR became the new standard in Europe for controlling user marketing data. In the US, Facebook, Google and others had embarrassing testimonials before Congress as legislators and individuals became outraged at the way in which their private data was being used for nefarious purposes.

AppsFlyer, and most trustworthy attribution providers do not share IDFA and GAIDs with other companies.

Instead, they create an anonymous pool of matched Web Cookies and IDFA/GAIDs.

They leverage this data set internally to match users across all their apps – without sharing this data explicitly with companies. This sets limitations on how matching works and how efficient it can be. Not all companies follow these implicit rules and respect user privacy laws.

That’s why choosing an attribution provider that is part of OpenGDPR is more important in 2019 than ever before.

attribution alert

In conclusion, don’t believe the hype that some vendors have “cracked the secret sauce”

When folks say that “legacy attribution systems can’t connect the dots”, this is largely sales and marketing jujitsu, and is not grounded in real technology.

All good attribution providers use deterministic web cookie and device ID pairs (IDFA or GAID) to match users and their touchpoints.

What matters more is the reach of the attribution providers pool, as well as how you, as an individual choose to leverage or not leverage deep links.

Attribution and deep linking tech
Chapter 2

Attribution and deep linking tech and taxonomy


The trouble with attribution and deep linking is that there are so many different names for the same thing.

Equally important, often marketers think in terms of user experience, but the technical delivery mechanism of that experience is lost. And especially relevant is even with the word “deep linking” – which marketers often interpret as driving somebody to a particular spot in the app, but as of the writing of this document – can be extremely nuanced.

Did they mean a URI scheme? An Apple Universal Link? A chrome intent? An Android App Link?

These are all deep links by definition, they all drive the person to the right spot in the app in certain contexts.

With this in mind, let’s review some of the most common deep linking and attribution taxonomy. You can and should use this when speaking with your team and across members of product, engineering and marketing.

A Chrome Intent is an Android-only deep linking mechanism that lets users launch the app from a webpage (documentation on how it works can be found here).

Deep linking and attribution, explained

A deep link is a link that takes a user to a specific spot on the website or the app.

All web URLs that go past the homepage are deep links.

A mobile deep link contains all the information needed to take a user directly to a particular location within an app, instead of just launching the app’s homepage offering a consistent, contextual experience from the promotion to the in-app landing page.


Attribution describes the process of identifying a set of user actions across screens and touch points, that contribute in desired outcome or event, and then identifying where that user came from – the source.

  • Web attribution is solved by the use of web query parameters and javascript. A link is clicked, and the team could use javascript, grabbing and storing the URL parameters (for example: window.location.href.split(“utm_campaign=”)[0]).
  • App attribution is much more complicated especially in the context of the Apple App Store as it does not allow you to pass query parameters through the download process. This means that every user on iOS is considered as a new user, unless you use a 3rd party attribution service.

3rd party attribution service:

A third party tool used to attribute and measure users using a combination of link click, probabilistic modeling, SDK, and people-based attribution services.


Matching refers to when a user opens the app, the 3rd Party Attribution Service’s SDK recognizes a set of parameters and “matches” the user.

Technically speaking, the SDK sends over a request to the server where the Attribution Service tries to look for the same device that visited the web link. If it finds a positive match, it returns the campaign information (Attribution) as well as the link or other data elements associated with the web side click.

This information can be leveraged for deep linking.

For example, if a URL is provided in the callback, the developer can parse out the web URL, URI or other parameter used to route the user to a particular spot in the app.


A Software Development Kit (i.e. a developer’s biggest foe) is usually required in order to properly attribute a user, as the 3rd Party Attribution Service’s SDK will collect and process the user information as well as receive the attribution data and make this presentable in a format that is easy to use (callback).

If you do not want to use an SDK, you would have to build the functionality using direct API calls.

In some cases, there are no public API calls and so choosing not to use an SDK means limiting functionality and capabilities. In general, attribution and deep linking as a service has become a commoditized engineering effort and it is much better to leverage a 3rd party than to try to build it yourself.


A callback is a response that is returned and passed as an argument to other code.

In the attribution and deep linking space, there is a callback to the app – or response back the app – once the 3rd Party Attribution Service matches the user and returns the attribution and deep link data.

The proper way to think about “types” of deep links is as “mechanisms” of deep linking.

A deep link is any link that routes a user to the right place in the app.

How this occurs, however, can vary greatly.


Universal Resource Indicator for the app that is sent in the app’s configuration.

This allows the app to be opened directly. “Routes” can be configured much like pathnames can be configured. Most apps have URI Schemes and Routes.

Examples of URIs include:

  • airbnb://
  • walmart://
  • fb://

The route is the part of the URI that comes after the base scheme.

Chrome Intent:

A Chrome Intent is an Android-only deep linking mechanism that lets users launch the app from a webpage.

Documentation on how it works can be found here.

Apple Universal Links are a standard from Apple that is deployed on the iPhone operating system (iOS), which allows a user to tap a link and be delivered immediately to the app (if it’s installed on their device). This is a system level route, as opposed to a redirect through a URL or link measurement service.

User intent:

Apple defines “User Intent” into their standard by enforcing that users who click on Apple Universal Links, and therefore are routed as such, must have user intent. In technical terms this is manifested by Apple Universal Links not working when wrapped in other click measurement or routing services – including email click recorders.

Apps and other in-app browsers, also can choose to respect Universal Links or not, which influences intent.

Click measurement (click wrapping):

Click wrapping is when a URL is redirected through another URL (or webpage) measurement purposes. This breaks Apple Universal Links, and complicates attribution and deep linking for email and paid channels.

Android App Links are Google’s version of Apple Universal Links and are deployed on the Android operating system. They allow a user to tap a link and be delivered immediately to the app (if it’s installed on their device).

This is a system level route, as opposed to a redirect through a URL or link service.

Deep linking chronology
Chapter 3

A deep linking chronology

Things weren’t always so complex.

Before there were Apple Universal Links, and Android App Links, and Chrome Intents, there was just plain vanilla URI Schemes.

In order to understand why things are so complex today, it’s important to understand how attribution and deep linking first worked, and how that has evolved to today.

Original deep linking

Originally, there were just links and URI schemes.

Then AppsFlyer pioneered the technology to attribute and deep link users.

Here’s how it worked:

  1. A user would click an attribution link (links that looked and worked much like they do today)
  2. The user would be redirected to a page where javascript would load
  3. In that brief moment, the browser would know the operating system (OS), it would have the platform configuration including the URI scheme and route, and so Javascript would set a timer and then try to fire the URI scheme and route
  4. If the timer expired before the browser responded, then it was known that the user did not download the app, and so the browser would redirect to the appropriate app store tied to that OS
  5. Just like with regular attribution, the user would install and open the app, passing on this information in the callback to the app
  6. If the user has the app, then the URI would have opened the app immediately, and when the URI was opened, the Attribution provider would also pass a clickId or linkId so that it’s SDK knew exactly where the user came from

This was a simple and clear way to deep link and was leveraged by all the major 3rd Party attribution and deep link services.

So what’s changed?

Everything changed in 2015 when Apple introduced Apple Universal Links as part of iOS 9.3.

In iOS 9.3 Apple introduced two things:

  1. The new standard for routing to apps, called Universal Links
  2. A new Safari change that got rid of non-blocking javascript, and therefore eliminating the ability on iOS to route as described in the previous section – the timer and javascript routing mechanism no longer worked on iOS

The old world was dead forever after iOS 9.3 was released.

From this point in time, the ability to attribute and deep link only became more complex as operating systems and media vendors released their own standards including Chrome Intents, Android App Links, Facebook App Links, and more.

Today, deep linking is highly segmented and very much depends on the operating system, the mechanism being used, as well as the browser or app context.

Here’s a look at the the expectations well-known deep linking contexts:

Facebook Newsfeeddeep links to af_web_dp or af_ios_url fallbackdeep links to app
Facebook Browserdeep links to appdeep links to app
Facebook Messengerdeep links to af_web_dp or af_ios_url fallbackdeep links to app
Facebook Messenger browserdeep links to appdeep links to app
Instagram Profiledeep links to af_web_dp or af_ios_url fallbackdeep links to app
Instagram Browserdeep links to appdeep links to app
Instagram Storiesdeep links to af_web_dp or af_ios_url fallbackdeep links to app
Twitter Feeddeep links to af_web_dp or af_ios_url fallbackdeep links to app
Twitter Browserdeep links to appdeep links to app
Redditdeep links to af_web_dp or af_ios_url fallbackdeep links to af_web_dp or af_android_url fallback
Pinterestdeep links to af_web_dp or af_ios_url fallbackdeep links to af_web_dp or af_android_url fallback
Pinterest Browserdeep links to appdeep links to app
Chrome Browserdeep links to appdeep links to app
Chrome Address Bardeep links to af_web_dp or af_ios_url fallbackdeep links to af_web_dp or af_android_url fallback.
Safari Browserdeep links to appN/A
Safari Address Bardeep links to af_web_dp or af_ios_url fallbackN/A
Firefox Browserdeep links to af_web_dp or af_ios_url fallbackdeep links to app
Firefox Address Bardeep links to af_web_dp or af_ios_url fallbackdeep links to app
UC Browserdeep links to af_web_dp or af_ios_url fallbackdeep links to app
Naver Browserdeep links to af_web_dp or af_ios_url fallbackdeep links to app
Kakao Browserdeep links to af_web_dp or af_ios_url fallbackdeep links to app
Opera Browserdeep links to appdeep links to app
Apple iMessagedeep links to appN/A
Apple Business Chatdeep links to appN/A
Slackdeep links to appdeep links to app
WeChatdeep links to af_web_dp or af_ios_url fallbackdeep links to af_web_dp or af_android_url fallback
WhatsAppdeep links to appdeep links to app
Gmaildeep links to appdeep links to app
Deep linking mechanisms
Chapter 4

Deep linking mechanisms deep dives

Apple Universal Links (iOS) and Android App Links (Android) are essentially the same concept, but are often interchanged, or confused with other routing mechanisms.

It’s important to be explicit when you talk about these concepts, else you risk confusing or confounding different technologies which operate very differently.

Apple Universal Links are a standard from Apple that is deployed on the iPhone operating system (iOS), which allows a user to tap a link and be delivered immediately to the app if they already have downloaded it.

These type of links do not have redirects and it is a special system setup with some degree of technical complexity.

When the user taps the link, a roundtrip server call is made to Apple and the OS opens the app immediately without ever opening the browser or loading the URL.

Apple Universal Links

Universal Links have two key components:

  1. AASA: Apple App Site Association file hosted on the domain of the links which will open the app immediately if the user has the app (documented here).
  2. Associated Domains: A small code change in the Entitlements > Associated Domains file of the iOS app.

It’s important now to distinguish an essential point – Universal Links are not a “set of links” but rather a system that is setup on links that your domain uses.

Universal Links can be enabled on your own company domain URLs as well as 3rd Party attribution URLs.


  1. A link, which has “Universal Links” enabled on them:
  2. An AppsFlyer OneLink, which has “Universal Links” enabled on them:… af_dp=walmart://ip/Banana-Dog-Costume/172218418

In the top case, the entire URL will come into the app via continue UserActivity, and you can then parse and route the user accordingly.

In both cases, it is assumed that the developer will have some type of parsing and routing on each link. In the above case the developer might parse everything after the domain. In the bottom case, the developer will handle the af_dp value explicitly.

This is at the core of handling static value deep links.

As a final note, it is important to point out that almost every company’s AASA is hosted at their main domain followed by “/apple-app-site-association” and therefore you can easily find the AASA file for any company.

Android App Links is the equivalent linking system that is setup on Android.Apple Universal Links & Android App Links are essentially web URL which are intended to direct users to the optimal location on web or the app. They were intended to take users to mobile web if the user doesn’t have the app, but take the user to the exact content in the app if they do.

On a mobile device, if the user follows a universal link and has our app installed, they may be directed to the app, otherwise the system will fallback and land the visitor on our mobile website (with a few exceptions – see below).

For a link to be truly universal, it requires that the linked feature is enabled on web, iOS and Android and for all apps to share the same resource path.

Deep linking callbacks

Chapter 5

Handling attribution and deep linking callbacks (with examples!)

The easiest way to put some of this knowledge into practice is to demonstrate how to handle attribution and deep linking using examples.

Case 1: Handling standard attribution and routing

In this example, it is assumed that you already have the SDK setup and are properly following all steps to successfully test a deep link.

If you are not able to, check out the Testing & QA section of this guide which elaborately lay out exactly the process to follow on how to QA deep links, as well as some of the common pitfalls.

We also provide succinct checklists to follow with your QA team.

In this case, assuming you have setup your SDK and now you’re trying to understand how to deep link the user. AppsFlyer provides two different functions for receiving install data in the apps:

  • Android: onInstallConversionDataLoaded
  • iOS: onConversionDataReceived

The returned conversion data from these functions consists of all the parameters on the original attribution link and some additional server parameters created at the time of click or install.

Since the conversion data is reliant on the attribution link, it means that different sources and attribution links may produce different conversion data parameters.

Additionally, there are 3 possible outcomes depending on the type of install:

  1. Non-organic installs: Returns the original attribution data of the install (see the examples below).
  2. Organic install (or re-install): Returns “organic install”
  3. Re-attribution: Returns the re-attribution conversion details.

Example in action

Below is an example callback you might receive if you called onConversionDataReceived on iOS and received a proper callback.

The example is demonstrated with the shopping app, Jet, who uses AppsFlyer to deep link from paid ads.

  1. ▿ 0 : 2 elements
  2. – key : click_time
  3. – value : 2018-08-14 02:37:30.798
  4. ▿ 1 : 2 elements
  5. – key : orig_cost
  6. – value : 0.0
  7. ▿ 2 : 2 elements
  8. – key : cost_cents_USD
  9. – value : 0
  10. ▿ 3 : 2 elements
  11. – key : is_first_launch
  12. – value : 0
  13. ▿ 4 : 2 elements
  14. – key : campaign
  15. – value : Test
  16. ▿ 5 : 2 elements
  17. – key : af_click_lookback
  18. – value : 7d
  19. ▿ 6 : 2 elements
  20. – key : af_dp
  21. – value : jet://product/Carbona-Washing-Machine-Cleaner-3-Count/d1909b8fb76240d480df7c983452a913
  22. ▿ 7 : 2 elements
  23. – key : idfa
  24. – value : 00000-0000-0000-0000-0000000000000
  25. ▿ 8 : 2 elements
  26. – key : media_source
  27. – value : Email
  28. ▿ 9 : 2 elements
  29. – key : install_time
  30. – value : 2018-08-14 02:38:40.012
  31. ▿ 10 : 2 elements
  32. – key : af_status

A few things should stand out immediately and it should be clear that you can use this key value structure in the callback of this function to parse information and use it to route the user, and call custom events or experiences.

For example, the af_dp value, which stands for AppsFlyer deep link, can be leveraged to immediately route the user to this URI.

Similarly, the is_first_launch value can be used to determine whether this is the first time the user launched the app after install, or whether the user already opened the app in the past.

A unique aspect of AppsFlyer is that it has another method for both iOS and Android – onAppOpenAttribution to receive attribution data every time the app opens.

The below flow diagram outlines exactly how you could use these two different functions in combination to receive install data when an install occurs, and to receive open data when it does not. From there, you could parse the different keys that you’d like to use for deep linking such as af_dp, af_web_dp, af_ios_url, or af_android_url.

A full list of AppsFlyer link parameters that would be included in a URL can be found here.

Attribution and routing with AppsFlyer

This brings us to an important nuance: depending on the SDK and the 3rd Party Attribution Service, you may have different ways of receiving data.

AppsFlyer has two distinct methods as illustrated by this chart. Others might have more or less.

The process by which you parse data from the callback and route the user, however, should not change!

Case 2: AppsFlyer and more with a CDP (mParticle)

In the case below, we will examine how to handle multiple deep link responses via a Customer Data Platform – in this example, mParticle.

The key to this use case is to first, make sure you understand the response keys from each vendor, then to make sure you prioritize who to handle from a deep linking perspective.

Last, but not least, we’ll examine how you can log this attribution callback data to a custom event so that you can trigger a push notification, pop up promotion or other campaigns using your AppsFlyer data!

Example in action

This app has integrations with two other vendors that provide callbacks to the app. mParticle, a customer data platform that excels in web and mobile data handling, pipes all attribution and deep linking callback data to a single method.

This single mParticle API wraps both of AppsFlyers callbacks – as well as the callbacks of all other attribution providers –  while the respective AppsFlyer Kit exposes constants to inform you of which callback has been fired.

On both platforms, the iOS/Android kit will register a delegate/callback with the AppsFlyer SDK on initialization and for the lifetime of the app’s process, and will call your completion handler block (iOS), or AttributionListener (Android), whenever there is a new conversion data available.

The keys returned in these results will match the result of the AppsFlyer SDK, documented here: AppsFlyer Callback Response.

  1. (lldb) po linkInfo
  2. ▿ 1 element
  3. ▿ 0 : 2 elements
  4. ▿ key : AnyHashable(“mParticle-AppsFlyer App Open Result”)
  5. – value : “mParticle-AppsFlyer App Open Result”
  6. ▿ value : 1 element
  7. ▿ 0 : 2 elements
  8. – key : link
  9. – value :
    (lldb) po linkInfo
  10. ▿ 1 element
  11. ▿ 0 : 2 elements
  12. ▿ key : AnyHashable(“mParticle-AppsFlyer Attribution Result”)
  13. – value : “mParticle-AppsFlyer Attribution Result”
  14. ▿ value : 11 elements
  15. ▿ 0 : 2 elements
  16. – key : click_time
  17. – value : 2018-08-14 02:37:30.798
  18. ▿ 1 : 2 elements
  19. – key : orig_cost
  20. – value : 0.0
  21. ▿ 2 : 2 elements
  22. – key : cost_cents_USD
  23. – value : 0
  24. ▿ 3 : 2 elements
  25. – key : is_first_launch
  26. – value : 0
  27. ▿ 4 : 2 elements
  28. – key : campaign
  29. – value : Test
  30. ▿ 5 : 2 elements
  31. – key : af_click_lookback
  32. – value : 7d
  33. ▿ 6 : 2 elements
  34. – key : af_dp
  35. – value : jet://product/Carbona-Washing-Machine-Cleaner-3-Count/d1909b8fb76240d480df7c983452a913
  36. ▿ 7 : 2 elements
  37. – key : idfa
  38. – value : 0000000-0000-0000-0000-00000000000
  39. ▿ 8 : 2 elements
  40. – key : media_source
  41. – value : Email
  42. ▿ 9 : 2 elements
  43. – key : install_time
  44. – value : 2018-08-14 02:38:40.012
  45. ▿ 10 : 2 elements
  46. – key : af_status

In this case, we are receiving callback data from the single mParticle deep link API.

Just as in the case of handling the AppsFlyer SDK response directly, you should consider which one of these responses you want to handle and how you want to handle it.

Here is a framework for structuring your link logic:

  1. If you are receiving callback data from multiple vendors, who takes precedence? Most marketers prioritize the response from a paid-relevant, MMP qualified vendor like AppsFlyer.
  2. Remember to check both install and open responses from AppsFlyer.
  3. If the response is “Organic” you might choose to check the other vendors, especially if that vendor doesn’t have a direct integration with AppsFlyer.

In addition to thinking through your attribution and deep linking logic, you should also log a custom event with the flattened keys and values from this client side attribution event.

Below are samples of how to do this directly with AppsFlyer as well as with a CDP like mParticle.


iOS – (void) trackEvent:(NSString )eventName withValues:(NSDictionary)values

The eventName could be as simple as “Attribution” or “Attribution Data” and the values should be all or some of the values from the callback response listed above.

Android – public static void trackEvent(Context context, String eventName, Map eventValues)

The eventName could be as simple as “Attribution” or “Attribution Data” and the eventValues should be all or some of the values from the callback response listed above.

See further documentation on this for both OS’s here.



MPEvent *event = [[MPEvent alloc] initWithName:@”Attribution Data”
                                         type:MPEventTypeTransaction]; = @{@”key”:@”value”};
[[MParticle sharedInstance] logEvent:event]


Map<String, String> eventInfo = new HashMap<String, String>();
eventInfo.put(“key”, “value”);
MPEvent event = new MPEvent.Builder(“Attribution Data”, EventType.Navigation)

See further documentation for iOS and Andriod.

Deep linking advanced topics

Chapter 6

Advanced topics

Web-to-app strategy: Banners and links

Considering the driver of web to app is an important and often neglected part of an engineer’s thoughts around attribution.

Typically organizations get spun up around analyzing and thoughtfully considering the following problem:

If we drive users from Mobile Web to the app will that negatively impact overall revenue? How do I compare my Web users to my app users?

Typically when teams that have widespread Mobile Web adoption and are worried about its impact on revenue, it’s important to note that App users convert to purchase universally at a much higher rate, with 3x higher conversion rates.

This is largely because apps can deliver a UX that is much higher quality and easier to navigate through the purchase funnel. In addition to the overall ease of conversion on apps compared to mWeb.

Other factors play into why it’s important to drive users to the app:

  • Login

    When users are logged in on the app it makes it much easier for them to simply add something to their cart and checkout.

    Apps can retain login credentials for longer sessions, and the UX design of a native experience is easier and higher quality to web. This is why users spend more time and convert more in apps compared to Mobile Web.

  • Engagement

    In addition to having an easier and better experience in the app compared to Mobile Web, apps can leverage push notifications, message center, feeds and other native only elements to prompt users to purchase or pickup an abandoned cart. Email is the only option available for Mobile Web users to be prompted.

    This higher rate of activation can lead to better results.

Outside of the business benefits of driving users to the app from Mobile Web, there is justification from an attribution and technology perspective that it is highly advantageous to have a cohesive web to app strategy.

web-to-app banners

This is because People-Based Attribution, which was earlier discussed, in part relies on your company’s use of the same attribution system throughout your marketing stack – on web, in the app, on ads, and in emails – so that people are more appropriately and intelligently attributed across touchpoints.

As discussed, the more you measure with a single attribution system, the better you’re going to be able to identify the sources of traffic and the richer quality understanding you’ll develop of your users across platforms.

record users across all your marketing touchpoints

If you record users across all your marketing touchpoints using the same attribution tooling, you’ll get better attribution results and richer/more accurate data related to your users sources.

This is the fundamental premise of People Based Attribution, and an argument for why you should stick with one single Attribution provider for all your needs.

What does a “great” web-to-app strategy look like?

What does great web to app strategy even look like?

You’re convinced at this point that you need a banner, but how should you design it and what’s the easiest way to power it?

There are a few options:

  1. Custom design and custom measurement
  2. Out-of-the-box design and measurement
  3. A hybrid of the above

Option #1 is to design a banner and implement your own custom measurement not using an attribution tool at all.

There are lots of reasons why this would be disadvantageous, but the crux being that it is an immense amount of work when there are easier options available.

You maintain control at the expense of speed and flexibility. Here are some examples of fully custom banners that companies have made on their own:

Option #2 is the optimal and easiest approach: use an out of the box Web SDK with its design and measurement solutions that are available as JS methods.

AppsFlyer provides some static code that is used to generate the banner and some optional methods and parameters that can be used to customize the design and measurement.

The Web SDK has a method to create a banner that floats on the top of the website and provides options in the settings variable that customize the title, icon, call to action (CTA), as well as a long list of measurement parameter options that can be customized and set at a static and dynamic level using Javascript.

The benefit of this method is that the developer doesn’t need to worry about both designing the banner, ensuring it fits all browsers and device models and doesn’t have to worry about properly constructing and maintaining the attribution URL that floats under the button of the CTA.

In this case, AppsFlyer is creating the URL with the attribution settings and generating the link.

This is helpful as certain pages may have settings that vary dynamically, and so this out of both settings variable allows the developer to customize the experience and measure as the website changes.

Regardless of your choice, one thing is very certain – do not use the “out of box” meta tags provided by Apple and Android.

Not only do these provide limited to no measurement, which means no contribution to your overall Attribution model, but they provide no customization.

If you’re going to go to the trouble of installing some JS on your site, you might as well opt for a few extra minutes and install a banner that accomplishes more than sending users to the App Store.

install a banner

Deep linked emails


Ever since the advent of Apple Universal Links, countless marketers, product managers and engineers have lamented over emails that no longer deep link to the app.

This is partially due to the complexity of linking mechanisms that has been outlined in previous sections. Adding to the complexity is that there are so many different email clients that handle these mechanisms differently.

However, there is hope!

Deep linking with email is not as complex as people make it out to be.

There is one case that is hard to handle – iOS, when users have the app – but otherwise, nothing really changes for the vendor, client or user.

You can handle deep linking from email yourself, much like you handle routing from any other 3rd Party Attribution Service. It just requires an understanding of Apple Universal Links/Android App Links, a willingness to use AppsFlyer or other vendor links in your URLs, and a willingness to modify your client code to handle email links very specifically.

Let’s quickly recap why emails often don’t deep link users properly on newer operating systems:

  • ESPs wrap links in click recording
  • This breaks Apple Universal Links, and complicates Android
  • ESPs don’t provide reliable routing to app or web based on a variety of circumstances like a tool such as OneLink provides

The only real case where deep linking behaves poorly is when the user has the app on iOS.

In this case, the ESP link that measures clicks “breaks” the universal link functionality. You have to develop a system to handle this. This explains what the  problem is when people say “email deep links” or “universal links” are broken for email.

As a final note, people often ask, “well, what about desktop links?!” Desktop links behave normally in all cases. The ESP link just wraps whatever redirects you want. Redirects on desktop never go to any type of app and most Desktop experiences go to web still, so there is no modification needed.

email to app journey

So, there you have it – don’t believe the hype from vendors who try to sell you that deep linked email requires an expensive product.

Using attribution links or deep links from AppsFlyer or other standard 3rd Party Attribution Services should work easily and without an additional cost in almost all cases based on the following assumptions:

  1. You must set up deferred and regular deep linking. For AppsFlyer, this includes “OneLink”.
  2. You must set up Apple Universal Links and Android Apps Links with these individual vendors.

If you meet both of these requirements, then using regular deep links in your emails works for iOS and Android when the user doesn’t have the app.

In these cases, the user can either go to mobile web or the app store depending on your preference.

If the user has the app on Android, these links will either open the app immediately via a Chrome Intent or the ESP link will route to the attribution link which will open the app via a URI scheme. You could also replicate the exact same functionality on Android as on iOS using Android App Links.

  1. You will keep click recording turned on

  2. You will setup Apple Universal Links and Android App Links on your ESP domain. Most ESPs now support this in their documentation.

    For example, Sendgrid has very detailed instructions on how to enable this on their site:

  3. Once Universal Links are enabled, then the app will open from the ESP click recording domain.

    You now need a way to retrieve the underlying URL that was put into the email HTML by the marketing team. You can use Sendgrid’s out of box URL Resolution code snippet:


- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
   if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) {
       NSURL *encodedURL = userActivity.webpageURL;
       if (encodedURL == nil) {
           NSLog(@"Unable to handle user activity: No URL provided");
           return false;
       NSURLSession *session = [NSURLSession sharedSession];
       NSURLSessionDataTask *task = [session dataTaskWithURL:encodedURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
           if (response == nil || [response URL] == nil) {
               NSLog(@"Unable to handle URL: %@", encodedURL.absoluteString);
           // Now you have the resolved URL that you can
           // use to navigate somewhere in the app.
           NSURL *resolvedURL = [response URL];
           NSLog(@"Original URL: %@", resolvedURL.absoluteString);
       [task resume];
   return YES;


If you have written your app for Android, you can use HttpURLConnection to resolve the URL by setting setInstanceFollowRedirects to false.

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
   if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) {
       NSURL *encodedURL = userActivity.webpageURL;
       if (encodedURL == nil) {
           NSLog(@"Unable to handle user activity: No URL provided");
           return false;
       NSURLSession *session = [NSURLSession sharedSession];
       NSURLSessionDataTask *task = [session dataTaskWithURL:encodedURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
           if (response == nil || [response URL] == nil) {
               NSLog(@"Unable to handle URL: %@", encodedURL.absoluteString);
           // Now you have the resolved URL that you can
           // use to navigate somewhere in the app.
           NSURL *resolvedURL = [response URL];
           NSLog(@"Original URL: %@", resolvedURL.absoluteString);
       [task resume];
   return YES;

Developers have lots of options at this point. Because you will get back the underlying URL, you could choose to deep link off of your own web URLs and your attribution URLs.

ESP Link clicked by user in email…

  • Opens App via Apple Universal Links or Android App Link
  • Resolve URL
  • URL could be of a few varieties:
  • Handle URL like you might handle these links already!

As you can see, you are not constrained to buying a deep linked email product, or relying on a vendor in any way you wa

We recommend you at least handle the case of your own links and your preferred attribution and deep linking partner, but don’t waste time and money on product and services that pretend to simplify this well known technical system.

How it works (flow diagram)

Email Creation

  • Marketer inputs a URL into their email campaign (this could be a regular company domain URL or an AppsFlyer link)
  • Your email service provider such as Sendgrid wraps up this link when the email goes out
  • The email the end user sees shows this ESP URL – for example:
ESP deep linking flow

When a user taps this link on iOS Chrome or Android (or iOS, Safari, but doesn’t have the app) then things work just fine. There’s literally no difference. It’s just regular old link redirection.

Email redirect cases

When a user on iOS 9.3+ who has the app taps the link, if they’ve setup Universal Links or Android App Links on their click recording domain, then the app will open immediately.

In the diagram below you can see three separate cases:

  1. This is the flow of what has already been described as deferred deep linking.

    Without any type of 3rd party redirection on your email links, you can use AppsFlyer links out of box to direct users to web or the app store. After install they may be deep linked to the right place in the app without any additional client side handling.

  2. In this case, it’s assumed that 3rd party click recording is enabled with your email service provider.

    If you’re using AppsFlyer or your own domain URLs, a simple piece of client side code can help you unwrap the underlying URL and route the user to the appropriate place in the app.

    By default, when the user has the app, they will open to the app immediately via Universal Links or Android App Links, whereby you’ll need to unwrap the ESP URL and resolve. If they do not have the app, AppsFlyer or your own domain URL can take them to the appropriate spot in the App Store or on Mobile Web.

  3. If you’re in need of a simple solution, you can use AppsFlyer only to intelligently direct the user to the App when the user has it. AppsFlyer will fire a URI scheme from the browser if they know the user has the app.
email redirect cases with deferred deep linking

The app should employ the code snippet referenced above to retrieve the underlying URL.

If the underlying URL is an AppsFlyer link or one of your own links where the query params are listed out, then no further async calls are required. Just handle the link by parsing query params sending them to the app’s route handler.

Note: If the underlying URL is an AppsFlyer OneLink Short URL, then you would need to subsequently make an async request to AppsFlyer to retrieve the link metadata before being able to process is. For this reason, we usually recommend using long form URLs where the key values are visible in the URL to make routing easy and not require another roundtrip call.

If it’s any other type of URL you can handle it similarly.

Current issues with Apple Universal Links

Apple Universal Links is a useful technology that is proven to offer a better UX for users who have the app.

However, these links have a few important limitations to be aware of, otherwise  you and your product teams will be spending endless hours debugging issues that cannot be fixed. There are actually four core issues as of the writing of this document. They almost all only applicable to Apple Universal Links (iOS 12), not Android App Links.

Issue 1: No recording or attribution

Because Apple Universal Links are not a redirect, but rather a system applied to links to open the app, it is very difficult to establish true click recording.

Why? Because the app opens immediately from Apple Universal Links.

There’s no redirect through a webpage to count a click to a server. Instead, when the app opens, the URL that opens the app gets reported in via a well known Apple code snippet called `continueUserActivity`.

To count a click on the URL, a marketing team would have to setup a server and manually count the click from the app.

A much simpler and easier solution is to use an attribution and deep linking tool, such as AppsFlyer.

These systems will have Universal Links on their actual domain, such as and automatically execute this type of recording for you. In short, whenever you need to record users who click on links, you should use a 3rd party tool, like OneLink.

This problem goes by many names. Link wrapping, click recording, link redirect, they all refer to the same thing.

When a marketer runs an ad or sends an email, often the service they are using will take the link they are using and “wrap it” or send it through a redirect so that the system they are using can count a click. It does this by redirecting to their website before sending the user to the final destination URL that the marketer input into the system.

link wrapping and deep linking

Both Apple Universal Links and Android App Links cannot be wrapped in other URLs.

If you do this, then the link will redirect users to the web fallback instead of to the app.

This mostly impacts marketers running paid ads with services like Double Click (where URLs get wrapped) and email marketers who have click recording enabled with their Email Service Provider (ESP).

In both cases, other 3rd party tools may wrap URLs you are using that would otherwise behave as Universal Links.

This breaks the functionality and defeats the purpose of using Universal Links. There are a variety of solutions to this, from turning off click recording to employing a specialty consultant to help navigate the complexity.

Issue 3: Phantom banner syndrome

For some reason, Apple could randomly inject a banner ad onto your site in Safari when Universal Links is implemented. There is no way to control this, customize it, or record it. It’s a strange feature that Apple introduced with Universal Links and has confused the market quite a bit.

Most customers just choose to ignore this, as it is only displayed in a few cases, while a click on Apple Universal Links would normally take the user to your app and not your website.

phantom banner and deep linking

Issue 4: General performance instability

Starting with iOS 11, many apps have noticed that Universal Links do not always perform the same way. 

iOS leverages the domain information from the Apple App Site Association (AASA) file you’ve established in order to open apps with Apple Universal Links.

When a new user on iOS installs the app, the AASA file is downloaded into local storage on the user’s device, and is then parsed to configure the routing for Universal Links at the device level, allowing for a user to tap a link and route properly with a configured domain.

Unfortunately, on iOS 11.2, developers report that the AASA file no longer reliably downloads onto the device and into local storage after install, and therefore, since the file is not present, Universal Links don’t work. Generally, they are also known to be difficult to debug with many implementation steps to install.

There is a well known Apple bug regarding this issue that is being monitored which you can follow here:

Assuming you’ve setup Universal Links on your regular URLs, or on the URLs you use for your attribution provider, you can test this and possibly get Universal Links to work again by doing the following:

  1. Logout of app
  2. Delete app
  3. Install the app from the app store or test/QA site
  4. Restart the device (turn off and then on again) – on iOS 11 this can often fix strange entitlement issues where the behavior of the link is not correct when clicked
  5. Create or find a link you are trying to test
  6. Paste the link in Notes, iMessage or in an email using the Apple Mail Client
    • Do not use Slack, Facebook or any other app to click the link
    • Do not paste the link into Safari – this will not work correctly
  7. Click the link
  8. The app should open immediately, without a redirect through the browser, and route the user to the proper place in the app
Deep linking testing and QA
Chapter 7

Testing & QA

Whether it’s attribution or deep linking, or both, it is vital to your product and marketing teams to have extremely well documented, well understood QA and troubleshooting guidelines.

Testing attribution and deep linking is very complex and full of technical nuances:

  • What media source are you testing?
  • Are your query parameters accurate?
  • Is your link URI encoded?
  • Does the link have the appropriate API and Redirect Parameters?
  • Are you testing with Apple Search Ads turned on? Did you know this impacts callback latency?
  • Are you testing deferred or regular deep linking?
  • If regular deep linking, by what mechanism?
  • Are you clicking the link on Slack? If not, from what app or browser?
  • Are you using a test or production build?
  • Are your links configured with an AASA?
  • Is your AASA the correct format and includes the correct app prefix, bundle identifier and path that is associated with your test or prod app?

These are just some of the questions you must ask when QA’ing link.  

This guide outlines how to tackle these questions with both direct answers as well as a step by step process when testing links.

Process philosophy

  • Define a clear test process and goal.
  • Explicitly state every component of the process – leave nothing to assumption or speculation (the above questions are the types of questions you must ask) including: OS, OS version, QA app, app version, browser, links, how to click links, where to click links from, QA tester, vendor, owner, user experience.
  • Define expected outcomes by iOS, Android, Web X User State (IE Has or Doesn’t have the app). This should create a clear matrix so you can test all expected outcomes. Within this table make sure to denote the mechanism by which deep linking occurs.
  • Create a clear document that captures test cases – share and collaborate with team-members using the same document.
  • Always write down every required component of the documented QA process – failing to document is failure to QA properly.

Simulating fresh installs – iOS

In order to simulate a fresh install on iOS do the following in this order:

  1. Logout of the app.
  2. Delete the app.
  3. Clear your IDFA: Settings > Privacy > Advertising > Reset Advertising Identifier…
  4. Clear your Cookies: Settings > Safari > Clear History & Website Data
  5. Click on the attribution link – let it redirect you through to Safari landing page.
  6. Click “Open App Store”.
  7. Do not download the app from the app store unless you are testing the production app.
  8. Load test/QA version of the app on your device if applicable.
  9. Open the app.
  10. Depending on your setup, you should see a fresh install and a deep link if you are leveraging the callback to route to the proper spot in the app.

Testing URIs

Recall that a URI is a Universal Resource Indicator. It is a URL or link that points to an app. In technical terms, this means that you cannot access a URI unless you have the app.

On iOS, the easiest way to fire a URI scheme and or route, is to type it into Safari and hit “go.”

If you have the app, it will open immediately. If the URI and Route exists and is handled by the app, it should take you to the appropriate spot in the app.

testing URIs

If you do not have the app, you will see an error like this:

error testing URIs

Testing direct URIs

The below can be used to properly QA URI schemes and routes.

This should be part of every app’s standard QA process. You should maintain a known list of routes that are aligned on iOS and Android.

Here’s the process:

  1. Logout of app
  2. Delete the app
  3. Install and open the test or QA app in use
  4. Open the app
    1. On iOS, paste the URI into the browser on Safari
    2. On Android, put the link behind an <a href=””> element and click the button or link

On Android, it is often easiest to wrap the URI in an href element and host or put in an email or another type of medium. You cannot paste URIs into Chrome like you can with Safari.

For Attribution Link URIs – or URIs that are triggered by the 3rd Party Attribution Service – the process is very similar:

  1. Logout of app
  2. Delete the app
  3. Install and open the test or QA app in use
  4. Open the app
  5. Paste the link into email, notes, or SMS
  6. Click the link

Testing Universal Links

Apple Universal Links are possibly the hardest to test deep linking mechanism, not only because there are so many rules that could disable them, but also because there are known issues – one of which is not resolved and is a known bug with Apple.

Before reading below remember the following issues with Apple Universal Links:

  • Check your AASA. Make sure it’s accurate and hosted properly
  • Check your Entitlements File
  • Make sure the domain is correct and listed in the Associated Domains
  • Make sure you’ve filled in relevant information with your 3rd Party Attribution Service (with AppsFlyer, this UI can be found in the OneLink configuration menu)
  • Make sure the link you are using has the appropriate domain, and pathname
  • Make sure you’re not clicking the link from Slack or other non-supported apps or browsers (when in doubt, just use Notes or SMS app from Apple!)
  • Do not wrap your Universal Link in any type of click recording

Test Process:

  1. Get visual confirmation that the Universal Link domain is in the QA app Entitlements > Associated Domains.
  2. Navigate to the domain/apple-app-site-association and validate that the file and the proper pathnames are present.
  3. Delete all versions of the app from the device.
  4. Power the device off and then on – this resets the device’s entitlement and AASA cache.
  5. Install the test version of the app in question.
  6. Using the NOTES app or iMessage, paste a link into the application.
  7. Tap the link and note the following:
    1. You cannot use Slack, Outlook or other applications. Universal Links only work in some apps. Notes and iMessage are known, supported apps.
    2. Do not paste the link into the browser or use Chrome! Universal Links do not work in either of these situations.
  8. The app should open immediately without a redirect through the browser.
Ready to start making good choices?