--- title: "Email Universal Link Setup" slug: "email-universal-link-setup-1" description: "Explains how to set up a Universal Link so email links open your Android or iOS app directly instead of a browser, covering DNS, SSL, and asset files." updated: 2026-08-26T07:57:26Z published: 2026-08-26T07:57:26Z canonical: "academy.insiderone.com/email-universal-link-setup-1" --- > ## Documentation Index > Fetch the complete documentation index at: https://academy.insiderone.com/llms.txt > Use this file to discover all available pages before exploring further. # Email Universal Link Setup A universal link, also known as an email link that opens your app directly, is a special URL type that works in web browsers and mobile apps. It ensures a smooth experience no matter what device users use. With universal links, businesses can make it easy for users to open Android and iOS apps directly from emails and access content on any device with one link that works everywhere. Universal links work as follows: - **Opens app or web**: If a user has the app, the link opens the app. If not, it opens the web. - **Offers better user experience:** Users get to the right content quickly and easily. This article explains how the universal link setup process works, provides SSL setup examples for multiple providers such as AWS, Google Cloud, etc., and the following steps to complete the setup process: [1. DNS record setup](/docs/email-universal-link-setup#1-dns-record-setup) [2. SSL setup for your link branding domain](/docs/email-universal-link-setup#2-ssl-setup-for-your-link-branding-domain) [3. Verification](/docs/email-universal-link-setup#3-verification) [4. Handling asset files](/docs/email-universal-link-setup#4-handling-asset-files) [5. Verification](/docs/email-universal-link-setup#5-verification) [6. Application](/docs/email-universal-link-setup#6-application) [7. Link resolution](/docs/email-universal-link-setup#7-link-resolution) > If you want to complete the universal link setup, you need to [create a ticket](/v1/docs/create-a-support-ticket) for Insider One's team. ## 1. DNS record setup Insider deliverability team will share two **CNAME** records with you to add under your domain. Add the respective **CNAME** records in your domain hosting provider settings and inform the deliverability team. | Type | Host | Value | | --- | --- | --- | | CNAME | url{rand-id-1}.{your-domain}.com | sendgrid.net | | CNAME | {rand-id-2}.{your-domain}.com | sendgrid.net | Or | Type | Host | Value | | --- | --- | --- | | CNAME | {subdomain}.{your-domain}.com | sendgrid.net | | CNAME | {rand-id-2}.{your-domain}.com | sendgrid.net | Replace the placeholders with your own specific URLs. *E.g., url123.acme.com, 12334.acme.com.* > Below you can see DNS record setup tutorials for some providers: > > - [Cloudflare](https://www.youtube.com/watch?v=PYSIt3fEEoI) > - [AWS Route53](https://www.youtube.com/watch?v=dRoaj0QDjg0) > - [Google Domains](https://www.youtube.com/watch?v=t0vbawaPUN4) > - [GoDaddy](https://www.youtube.com/watch?v=x9ueVrFlIbM) > - [Azure](https://www.youtube.com/watch?v=uumSt8VLM7o) ## 2. SSL setup for your link branding domain To set up SSL for your link branding domain, proceed to **2.1 or** **2.2**. If you are using one of the **providers** listed in 2.1 (e.g., AWS Route53, GoDaddy, Azure, etc.), proceed to follow section **2.1.** If you're using a **CDN**, proceed to follow section **2.2**. 2.1. **Follow these steps if you are using one of the providers listed below (e.g., AWS Route53, GoDaddy, etc.).** You must set up SSL for your link branding domain (e.g., url123.yourdomain.com) as you would for the other domains. SSL setup will vary depending on the service provider and the available certificate. You need a certificate valid for the CNAME record shared with you. If you already have a wildcard (*.yourdomain.com) SSL that covers any subdomains, you can easily set it up. However, if you choose to use a double subdomain (url123.sub.yourdomain.com) as your link branding and you do not have a double wildcard certificate (*.*.yourdomain.com) or your SSL certificate is exclusive to your main domain (yourdomain.com), you will need to purchase a certificate to complete the setup. > Below you can see SSL setup tutorials for some providers: > > - [AWS Route53](https://www.youtube.com/watch?v=ookzXuMr8eY) > - [Google Domains](https://www.youtube.com/watch?v=245ZJLm1AV4&t=1477s) > - [GoDaddy (CPanel)](https://www.youtube.com/watch?v=h-CN_JVi0kU) > - [Azure](https://learn.microsoft.com/en-us/azure/app-service/configure-ssl-certificate?tabs=apex) > - [NGINX](https://gist.github.com/omermevlut/23de878d274eade0580ec93efb95fcf4) 2.2. **Follow these steps if you are using CDN.** Forward all the requests coming to url123.yourdomain.com to sendgrid.net. You can forward the request using your own CDN or Proxy. > Below you can see the CDN/Proxies documents for some providers: > > - [Cloudfront](https://support.sendgrid.com/hc/en-us/articles/4412701748891-How-to-configure-SSL-for-click-tracking-using-CloudFront) > - [Cloudflare](https://www.twilio.com/docs/sendgrid/ui/sending-email/content-delivery-networks/using-cloudflare-as-your-content-delivery-network-cdn) > - [Fastly](https://www.twilio.com/docs/sendgrid/ui/sending-email/content-delivery-networks/using-fastly-as-your-content-delivery-network-cdn) > - [KeyCDN](https://www.twilio.com/docs/sendgrid/ui/sending-email/content-delivery-networks/using-keycdn-as-your-content-delivery-network-cdn) > - [Sendgrid documentation for proxy setup](https://www.twilio.com/docs/sendgrid/ui/account-and-settings/custom-ssl-configurations) > - [NGINX](https://gist.github.com/omermevlut/23de878d274eade0580ec93efb95fcf4) At this point, your DNS records should look like the following example table: | Type | Host | Value | | --- | --- | --- | | CNAME | url123.yourdomain.com | URL to your CDN or Proxy or API Gateway | | CNAME | 123456.yourdomain.com | sendgrid.net | Your title goes here Do not change the CNAME for 123456.yourdomain.com; it should still point to sendgrid.net. 123456 is an example, and it can vary in your implementation. ## 3. Verification Verification consists of two steps: verification on your end and verification on Insider. Once you complete the previous steps, you can verify that everything is set up correctly on your end. To verify on your end, you will need to run the following command: ```Verification dig ns url{rand-id}.{yourdomain}.com // or dig ns {subdomain}.{yourdomain}.com ``` ![](https://cdn.document360.io/c6df4583-da94-4cb2-bb8a-be0cbdd11109/Images/Documentation/unnamed%20(98)(1).png) ### Verify Certificate To verify the certificate, run the following command: ```Verifycertificate openssl s_client -showcerts -connect url{rand-id}.{yourdomain}.com:443 // or openssl s_client -showcerts -connect {subdomain}.{yourdomain}.com:443 ``` You will receive a public certificate as a response with the following response: ![](https://cdn.document360.io/c6df4583-da94-4cb2-bb8a-be0cbdd11109/Images/Documentation/unnamed%20(99)(1).png) Once you've verified everything on your end, contact the Insider One team to verify your setup. Once Insider verifies the setup, your next email will include your custom domain in the links instead of sendgrid.net. ## 4. Handling asset files For universal links to work, include your application-specific asset files under the subdomain you set up in the [DNS record setup step.](/docs/email-universal-link-setup#1-dns-record-setup) Your setup must include route prioritization to re-route the incoming requests to assetlinks.json (Android) and apple-app-site-association (iOS) to your file server (or cloud storage) and everything else to sendgrid.net. Adding the asset files under the subdomain will ensure that your app is opened instead of the web page every time it is clicked. For example, if your links are branded as https://links.acme.com, your asset files should be available as https://links.acme.com/.well-known/assetlinks.json, https://links.acme.com/.well-known/apple-app-site-association, and https://links.acme.com/apple-app-site-association (without the JSON extension). The sample case above would look as follows: | Precedence | Path | Origin | Protocol | | --- | --- | --- | --- | | 0 | apple-site-association | File Server | HTTPS Only | | 1 | .well-known/apple-site-association | File Server | HTTPS Only | | 2 | .well-known/assetlinks.json | File Server | HTTPS Only | | 3 | * | sendgrid.net | HTTP and HTTPS | Below are examples of different providers. ### NGINX ```NGINX server {  listen 80;  listen 443 ssl;  server_name 'links.acme.com';  ssl_certificate '/etc/pki/tls/certs/links.acme.com.crt';  ssl_certificate_key '/etc/pki/tls/private/links.acme.com.key';  location = /apple-app-site-association {    root '/var/www/links.acme.com';    default_type 'application/json';  }  location = /.well-known/apple-app-site-association {    root '/var/www/links.acme.com';    default_type 'application/json';  }  location = /.well-known/assetlinks.json {    root '/var/www/links.acme.com';    default_type 'application/json';  }  location / {    proxy_pass 'https://sendgrid.net';    proxy_set_header 'Host' 'links.example.com';  } } ``` ### AWS Route53 Refer to the [Universal Link Setup page](https://www.twilio.com/docs/sendgrid/ui/sending-email/universal-links) to set up universal links using Route53, S3, and CloudFront. ### GCP For Google Cloud, you can utilize [routes](https://cloud.google.com/vpc/docs/routes) or use [URL maps](https://cloud.google.com/load-balancing/docs/url-map) on your load balancer to handle the traffic. ## 5. Verification If you set up your asset configuration internally, you can use a bastion server. If it's a public setup, you can directly test your asset links and routing. In either case, testing will be the same. ```Verification [must return JSON response] curl http://internal.domain/.well-known/assetlinks.json curl http://internal.domain/.well-known/apple-app-site-association curl http://internal.domain/apple-app-site-association [must return 404 response from sendgrid.net] curl http://internal.domain/test-404-response ``` ## 6. Application Once you’ve verified that the asset links are set up correctly, you can replace sendgrid.net in your DNS setup with your internal setup. ```Application links.acme.com => sendgrid.com // will be updated as links.acme.com => example.internal.load-balancer.com // or links.acme.com => cloudfront.com/12345 // or links.acme.com => 127.0.0.1 (NGINX instance IP) ``` Verify that the same files and routing work through your subdomain (links.acme.com). If routing fails and the URLs are broken, avoid sending emails until you fix the issue. ## 7. Link resolution Now you can trigger your application directly from the email links. However, the links are encrypted and the destination is unavailable. **Link resolution** is the final step in the implementation so your application can redirect users to the correct pages. To complete this action, follow these three steps: 1. Send an HTTP request to the link the user clicks to resolve the destination link. 2. Obtain the destination from the sent request. 3. Redirect the user to the relevant page in your application. This ensures the user is redirected to the destination page and the click log is delivered to the Insider server. Below, you can see some examples: ### Android link resolution example ```java @Override protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    onNewIntent(getIntent()); } protected void onNewIntent(Intent intent) {    String action = intent.getAction();    final String encodedURL = intent.getDataString();    if (Intent.ACTION_VIEW.equals(action) && encodedURL != null) {        Log.d("App Link", encodedURL);        new Thread(new Runnable() {            public void run() {                try {                    URL originalURL = new URL(encodedURL);                    HttpURLConnection ucon = (HttpURLConnection) originalURL.openConnection();                    ucon.setInstanceFollowRedirects(false);                    URL resolvedURL = new URL(ucon.getHeaderField("Location"));                    Log.d("App Link", resolvedURL.toString());                }                catch (MalformedURLException ex) {                    Log.e("App Link",Log.getStackTraceString(ex));                }                catch (IOException ex) {                    Log.e("App Link",Log.getStackTraceString(ex));                }            }        }).start();    } } ``` ### iOS link resolution examples ```swift func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {    if userActivity.activityType == NSUserActivityTypeBrowsingWeb {        guard let encodedURL = userActivity.webpageURL else {            print("Unable to handle user activity: No URL provided")            return false        }        let task = URLSession.shared.dataTask(with: encodedURL, completionHandler: { (data, response, error) in            guard let resolvedURL = response?.url else {                print("Unable to handle URL: \(encodedURL.absoluteString)")                return            }            // Now you have the resolved URL that you can            // use to navigate somewhere in the app.            print(resolvedURL)        })        task.resume()    }    return true } ``` ```objectivec - (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);                return;            }            // 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’re using an external company to handle your universal links such as *branch.io*, you might want to consult with the respective vendors on how to set up the universal links. If you have a mixed setup where you use both your own links and an external company setup, you need to complete all the steps listed in this guide and contact your provider to set it up with them as well. ## Related - [Email Link Branding Setup](/link-branding-setup.md)