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.
Navigate to Amazon CloudFront. Once you have created an account or are logged into your existing account, create a new S3 bucket and give it a unique name (e.g. links-example-com)
Upload your "apple-app-site-association" file into the root of the new S3 bucket
Under Permissions on the uploaded file, add a permission for Everyone to Open/Download (or Read in the new S3 UI), then hit Save
Under Metadata on the uploaded file, change the Content-Type
value to application/json, then hit Save
Create a new folder in the bucket called ".well-known"
Inside of the ".well-known" folder, upload the same "apple-app-site-association" file as in step 2
As above, add a permission for Everyone to Open/Download (or Read in the new S3 UI) and change the Content-Type
to "application/json"
Inside of the ".well-known" folder, upload your "assetlinks.json"
Repeat step 7 for your "assetlinks.json" file: add a permission for Everyone to Open/Download (or Read in the new S3 UI) and change the Content-Type
to "application/json"
Navigate to the AWS Certificate Manager
Request a new certificate for the domain your link branding is configured for (e.g. links.example.com)
AWS will send an email to the appropriate domain owners, requesting them to approve the certificate
Ensure that the certificate is approved and issued
Navigate to AWS CloudFront
Create a new Distribution that is a Web delivery method
Under the Origin Settings section, set the fields as follows:
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
Once the distribution is created, click into Distribution Settings
Under the Origins tab, create a new origin with the following details
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
Click Create
Create a third behavior with the following details
Hit Create
Ensure that the Behaviors are sorted so that the Default is the last onNewIntent
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)
Verify behavior using https://www.branch.io/glossary/universal-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.
Request a new certificate for the domain your link branding is configured for (e.g. links.example.com)
Place the certificate chain into the file named /etc/pki/tls/certs/links.example.com.crt
Place the private key into the file named /etc/pki/tls/private/links.example.com.key
Create the following directory /var/www/links.example.com
Create the file /var/www/links.example.com/apple-app-site-association, with the appropriate content for your apple-app-site-association file, as explained in Apple's Developer Documentation.
Create the directory /var/www/links.example.com/.well-known
Create the file /var/www/links.example.com/.well-known/apple-app-site-association, with the appropriate content for your apple-app-site-association file
Create the file /var/www/links.example.com/.well-known/assetlinks.json, with the appropriate content for your digital asset links file, as explained in Google's Developer Documentation.
Create the file /etc/nginx/conf.d/links.example.com.conf, with the following content:
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}