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.
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.
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.
There are several requirements that you must complete before you can begin using universal links in your email:
To keep your app secure, Google and Apple want to restrict which resources or websites are allowed to link directly to different pages within your app. This prevents bad actors from using universal links to gain access to sensitive information within your app.
Your "apple-app-site-association" and "digital asset links" files serve as secure means of authenticating your universal links; they verify that your website is allowed to open up a page within your app.
You must create your own digital asset links and apple-app-site-association files, and you must upload these files to a secure server.
Both "apple-app-site-association" and "digital asset links" files are comprised of a series of JSON key/value pairs that associate external URLs with pages within your application.
For detailed instructions on how to configure an iOS "apple-app-site-association" file, please see Apple's Developer Documentation.
For detailed instructions on how to configure an Android "digital asset links" file, please visit Google's Developer Documentation.
1{2"applinks": {3"apps": [],4"details": [5{6"appID": "[YOUR APP ID HERE]",7"paths": [8"/uni/*"9]10}11]12}13}
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.
Do not append the JSON file extension to your apple-app-site-association file!
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]
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 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 or NGINX.
After creating your iOS "apple-app-site-association" file and/or your Android "digital asset links" file, you need to host them on a secure content delivery network. The following instructions will guide you through setting up Amazon's CloudFront to host these files.
Content-Type
value to
application/json
, then hit
Save
Content-Type
to "application/json"
Content-Type
to "application/json"
1* **Origin Domain Name:** sendgrid.net2* **Origin ID:** sendgrid.net3* **Origin SSL Protocols:** only TLSv1.24* **Origin Protocol Policy:** HTTPS Only
17. Under the Default Cache Behavior Settings section, set the fields as follows:
1* **Cache Based on Selected Request Headers:** All2* **Query String Forwarding and Caching:** Forward all, cache based on all
18. Under the Distribution Settings section, set the fields as follows:
1* **Alternate Domain Names:** links.example.com2* **SSL Certificate:** Custom SSL Certificate, pointing to the appropriate ACM certificate
19. Hit Create Distribution
1* **Origin Domain Name:** links-example-com.s3.amazonaws.com2* **Origin ID:** s3
22. Click Create
1* **Path Pattern:** apple-app-site-association2* **Origin:** s33* **Viewer Protocol Policy:** HTTPS Only
24. Click Create
Create another behavior with the following details
Create a third behavior with the following details
30. Wait for the distribution to deploy
Verify that the distribution serves up the expected files (do this without changing the real DNS to avoid causing any issues with existing links)
After creating your iOS "apple-app-site-association" file and/or your Android "digital asset links" file, you need to host them on a secure content delivery network. The following instructions will guide you through setting up NGINX to host these files.
1server {2listen 80;3listen 443 ssl;4server_name 'links.example.com';5ssl_certificate '/etc/pki/tls/certs/links.example.com.crt';6ssl_certificate_key '/etc/pki/tls/private/links.example.com.key';7location = /apple-app-site-association {8root '/var/www/links.example.com';9default_type 'application/json';10}11location = /.well-known/apple-app-site-association {12root '/var/www/links.example.com';13default_type 'application/json';14}15location = /.well-known/assetlinks.json {16root '/var/www/links.example.com';17default_type 'application/json';18}19location / {20proxy_pass 'https://sendgrid.net';21proxy_set_header 'Host' 'links.example.com';22}23}
It is important to make sure that only the links within your email that point to your app are flagged as universal links.
It is not unusual to include links to pages outside of your app alongside links to your app in the same email. Not all of these links should be treated as universal links. For example, if you have Facebook or Twitter links tagged as universal links, users will be taken to your app when they click those links instead of being taken to your Facebook and Twitter pages.
To flag links to your app as universal links, simply include the attribute universal="true"
within the HTML link of your email.
SendGrid adds the /uni/
parameter to flagged universal links
For example:
<a href="links.example.com" universal="true">Link to your app!</a>
This way, as long as your association file has the paths
restricted to /uni/*
as we recommend above, only the links that you want to be handled by your app will be.
If you exclude the universal="true"
attribute, your links will still function, but they will take your recipient to their mobile browser.
If you exclude the /uni/*
path in your apple-app-site-association
, the all links for your authenticated domain will be forwarded for your app to handle, which may cause issues.
Now that you've successfully set up your app to open SendGrid click tracking links, you'll want to ensure that your app handles them properly. The link your app receives will be the SendGrid encoded link, so you'll want to resolve the link in order to:
The following code examples help to illustrate what logic should be included within your own app to guarantee that your links are resolved, and tracked by SendGrid.
If you have written your app for iOS, you can use NSURLSession
resolve the link.
For example:
1func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {2if userActivity.activityType == NSUserActivityTypeBrowsingWeb {3guard let encodedURL = userActivity.webpageURL else {4print("Unable to handle user activity: No URL provided")5return false6}7let task = URLSession.shared.dataTask(with: encodedURL, completionHandler: { (data, response, error) in8guard let resolvedURL = response?.url else {9print("Unable to handle URL: \(encodedURL.absoluteString)")10return11}12// Now you have the resolved URL that you can13// use to navigate somewhere in the app.14print(resolvedURL)15})16task.resume()17}18return true19}
1- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {2if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) {3NSURL *encodedURL = userActivity.webpageURL;4if (encodedURL == nil) {5NSLog(@"Unable to handle user activity: No URL provided");6return false;7}8NSURLSession *session = [NSURLSession sharedSession];9NSURLSessionDataTask *task = [session dataTaskWithURL:encodedURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {10if (response == nil || [response URL] == nil) {11NSLog(@"Unable to handle URL: %@", encodedURL.absoluteString);12return;13}14// Now you have the resolved URL that you can15// use to navigate somewhere in the app.16NSURL *resolvedURL = [response URL];17NSLog(@"Original URL: %@", resolvedURL.absoluteString);18}];19[task resume];20}21return YES;22}
If you have written your app for Android, you can use HttpURLConnection
to resolve the URL by setting setInstanceFollowRedirects
to false
.
For example:
1@Override2protected void onCreate(Bundle savedInstanceState) {3super.onCreate(savedInstanceState);4setContentView(R.layout.activity_main);5onNewIntent(getIntent());6}78protected void onNewIntent(Intent intent) {9String action = intent.getAction();10final String encodedURL = intent.getDataString();11if (Intent.ACTION_VIEW.equals(action) && encodedURL != null) {12Log.d("App Link", encodedURL);13new Thread(new Runnable() {14public void run() {15try {16URL originalURL = new URL(encodedURL);17HttpURLConnection ucon = (HttpURLConnection) originalURL.openConnection();18ucon.setInstanceFollowRedirects(false);19URL resolvedURL = new URL(ucon.getHeaderField("Location"));20Log.d("App Link", resolvedURL.toString());21}22catch (MalformedURLException ex) {23Log.e("App Link",Log.getStackTraceString(ex));24}25catch (IOException ex) {26Log.e("App Link",Log.getStackTraceString(ex));27}28}29}).start();30}31}