Skip to contentSkip to navigationSkip to topbar
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
1
{
2
"applinks": {
3
"apps": [],
4
"details": [
5
{
6
"appID": "[YOUR APP ID HERE]",
7
"paths": [
8
"/uni/*"
9
]
10
}
11
]
12
}
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
1
[
2
{
3
"target": {
4
"namespace": "android_app",
5
"package_name": "[YOUR APP'S PACKAGE NAME]",
6
"sha256_cert_fingerprints": [
7
"[YOUR APP FINGERPRINT HERE]"
8
]
9
},
10
"relation": [
11
"delegate_permission/common.handle_all_urls"
12
]
13
}
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
1
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
2
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
3
guard let encodedURL = userActivity.webpageURL else {
4
print("Unable to handle user activity: No URL provided")
5
return false
6
}
7
let task = URLSession.shared.dataTask(with: encodedURL, completionHandler: { (data, response, error) in
8
guard let resolvedURL = response?.url else {
9
print("Unable to handle URL: \(encodedURL.absoluteString)")
10
return
11
}
12
// Now you have the resolved URL that you can
13
// use to navigate somewhere in the app.
14
print(resolvedURL)
15
})
16
task.resume()
17
}
18
return true
19
}
1
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
2
if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) {
3
NSURL *encodedURL = userActivity.webpageURL;
4
if (encodedURL == nil) {
5
NSLog(@"Unable to handle user activity: No URL provided");
6
return false;
7
}
8
NSURLSession *session = [NSURLSession sharedSession];
9
NSURLSessionDataTask *task = [session dataTaskWithURL:encodedURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
10
if (response == nil || [response URL] == nil) {
11
NSLog(@"Unable to handle URL: %@", encodedURL.absoluteString);
12
return;
13
}
14
// Now you have the resolved URL that you can
15
// use to navigate somewhere in the app.
16
NSURL *resolvedURL = [response URL];
17
NSLog(@"Original URL: %@", resolvedURL.absoluteString);
18
}];
19
[task resume];
20
}
21
return YES;
22
}
1
@Override
2
protected void onCreate(Bundle savedInstanceState) {
3
super.onCreate(savedInstanceState);
4
setContentView(R.layout.activity_main);
5
onNewIntent(getIntent());
6
}
7
8
protected void onNewIntent(Intent intent) {
9
String action = intent.getAction();
10
final String encodedURL = intent.getDataString();
11
if (Intent.ACTION_VIEW.equals(action) && encodedURL != null) {
12
Log.d("App Link", encodedURL);
13
new Thread(new Runnable() {
14
public void run() {
15
try {
16
URL originalURL = new URL(encodedURL);
17
HttpURLConnection ucon = (HttpURLConnection) originalURL.openConnection();
18
ucon.setInstanceFollowRedirects(false);
19
URL resolvedURL = new URL(ucon.getHeaderField("Location"));
20
Log.d("App Link", resolvedURL.toString());
21
}
22
catch (MalformedURLException ex) {
23
Log.e("App Link",Log.getStackTraceString(ex));
24
}
25
catch (IOException ex) {
26
Log.e("App Link",Log.getStackTraceString(ex));
27
}
28
}
29
}).start();
30
}
31
}