Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Universal Links


(warning)

Warning

The majority of this setup and management occurs outside of the SendGrid console. SendGrid support can only help with the steps that happen within your SendGrid account - like sender authentication setup. The setup options below are examples, and there are several more CDNs you could use to set up universal links.

Mobile devices are increasingly becoming the preferred method of receiving, reading, and engaging with email. If you send an email containing a link to your website, but you also have a corresponding mobile application, it is possible to ensure that any recipients who click the link on their mobile device are taken directly to your app instead of their web browsers.

This is accomplished by using universal links. A universal link is a unique URL that can be configured to open a window in either the recipient's web browser, mobile browser, or mobile application depending on the device the recipient is using. SendGrid enables you to simply tag individual links that you would like to be converted to universal links, without sacrificing the ability to track clicks on those links.

(information)

Info

These links are sometimes referred to as "deep links" in the context of Google's Android OS. Apple uses the term "universal links".

Regardless of the OS you are configuring your links for, we will use the term "universal links".

When setting up universal links for your app, it is important to ensure that you maintain the ability to track when users click those links. After configuring your universal links, we will explain how to ensure that your universal links are tracked.

(error)

Danger

Marketing Campaigns does not support universal links by default! If you would like to include universal links in your campaign, you must ensure that you edit the HTML of your template to appropriately flag your links as universal.


Requirement

requirement page anchor

There are several requirements that you must complete before you can begin using universal links in your email:

  • Universal links for iOS require an "apple-app-site-association" JSON file.
  • Universal links for Android require that you set up a "digital asset links" JSON file, along with configuring intent filters in your Android app's manifest file.
  • Your apple-app-site-association and digital asset links files must be hosted on an HTTPS web server or content delivery network (CDN).
  • To ensure that your universal links register click tracking events, and to ensure that your recipient is taken to the correct page within your app, you must properly resolve your links .
  • You must complete the link branding process for your account. When branding your links, you must use the same domain that will be used for your universal links. (e.g. links.example.com)
  • On iOS, you must include your branded link subdomain in the "Associated Domains" for your app. You can customize your subdomain using the custom return path in advanced settings while setting it up. Using the example above, you'd need to add an entry for "applinks
    .example.com" like this:
Universal Links IOS.

Example apple-app-site-association file

example-apple-app-site-association-file page anchor

_13
{
_13
"applinks": {
_13
"apps": [],
_13
"details": [
_13
{
_13
"appID": "[YOUR APP ID HERE]",
_13
"paths": [
_13
"/uni/*"
_13
]
_13
}
_13
]
_13
}
_13
}

(information)

Info

When configuring your universal links in iOS, you specify which paths you want to be handled by the app by using the paths argument in the apple-app-site-association file. You must flag your universal links with the attribute universal=true as documented here. In your apple-app-site-association, by adding ["/uni/*"] into paths, it ensures your flagged universal links clicks are properly tracked by SendGrid and are handled by the app appropriately.

(error)

Danger

Do not append the JSON file extension to your apple-app-site-association file!

Example assetlinks.json file

example-assetlinksjson-file page anchor

_14
[
_14
{
_14
"target": {
_14
"namespace": "android_app",
_14
"package_name": "[YOUR APP'S PACKAGE NAME]",
_14
"sha256_cert_fingerprints": [
_14
"[YOUR APP FINGERPRINT HERE]"
_14
]
_14
},
_14
"relation": [
_14
"delegate_permission/common.handle_all_urls"
_14
]
_14
}
_14
]

(information)

Info

When configuring your universal links in iOS, you specify which paths you want to be handled by the app by using the paths argument in the apple-app-site-association file. By specifying only the path ["/uni/*"], and using the universal=true attribute on your links as documented below, only appropriate links will be handled by the app, and others will be opened in the phone's browser.

Android requires that you specify these paths inside your app, rather than the assetlinks.json file. This is accomplished by adding intent filters for specific hosts and paths. Please visit Google's Android Developer Documentation(link takes you to an external page) to learn how to add an intent filter to your app manifest that can handle your universal links.

Once you have created and configured your Android and iOS configuration files, you will have to host them on a secure HTTPS server. Keep reading below to learn how you can host these files on either Amazon CloudFront(link takes you to an external page) or NGINX(link takes you to an external page).

Using Swift

using-swift page anchor

_19
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
_19
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
_19
guard let encodedURL = userActivity.webpageURL else {
_19
print("Unable to handle user activity: No URL provided")
_19
return false
_19
}
_19
let task = URLSession.shared.dataTask(with: encodedURL, completionHandler: { (data, response, error) in
_19
guard let resolvedURL = response?.url else {
_19
print("Unable to handle URL: \(encodedURL.absoluteString)")
_19
return
_19
}
_19
// Now you have the resolved URL that you can
_19
// use to navigate somewhere in the app.
_19
print(resolvedURL)
_19
})
_19
task.resume()
_19
}
_19
return true
_19
}


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


_31
@Override
_31
protected void onCreate(Bundle savedInstanceState) {
_31
super.onCreate(savedInstanceState);
_31
setContentView(R.layout.activity_main);
_31
onNewIntent(getIntent());
_31
}
_31
_31
protected void onNewIntent(Intent intent) {
_31
String action = intent.getAction();
_31
final String encodedURL = intent.getDataString();
_31
if (Intent.ACTION_VIEW.equals(action) && encodedURL != null) {
_31
Log.d("App Link", encodedURL);
_31
new Thread(new Runnable() {
_31
public void run() {
_31
try {
_31
URL originalURL = new URL(encodedURL);
_31
HttpURLConnection ucon = (HttpURLConnection) originalURL.openConnection();
_31
ucon.setInstanceFollowRedirects(false);
_31
URL resolvedURL = new URL(ucon.getHeaderField("Location"));
_31
Log.d("App Link", resolvedURL.toString());
_31
}
_31
catch (MalformedURLException ex) {
_31
Log.e("App Link",Log.getStackTraceString(ex));
_31
}
_31
catch (IOException ex) {
_31
Log.e("App Link",Log.getStackTraceString(ex));
_31
}
_31
}
_31
}).start();
_31
}
_31
}


Rate this page: