*** NOTE: ALL INFORMATION IS ACCURATE AT DATE OF PUBLISHING ***
If you use Google Analytics, you should be familiar with channels. As defined by Google, channels are rule-based definitions of your website’s traffic sources that let you monitor the performance of all of the channels sending traffic to your website. Someone might come to your website, but not via a link including UTM Parameters which can be used to uncover more information about how they found your site. Understanding the method or ‘channel’ used is important to know where your marketing budget has actually been spent on something helping generate new Leads. In this post, I will provide a way in which to gather that information to pass through via a submitted Realtime Marketing form and using browser session storage so you can start tracking your Lead Channels.
For this approach, you will need to use and understand the basics of the following:
- Adding new fields to the Lead table in D365
- Creating Realtime Marketing Forms
- Adding Realtime Marketing Forms to your website
- Google Tag Manager
If you’ve got an understanding of all of the above (or have access to someone that does), read on. This approach should be used for all of your RTM forms ideally, but is flexible enough to be used just for one or two forms if required. First, it would be worth reading up on the default channel group used in Google Analytics. This is where the list of channels comes from. There are 18 default channels in total but my solution accounts for only 11 of them based on the niche options or unique channels include in the list that I am unable to truly test. You should be able to use my script as a good starting point, then follow the logic in the definitions and add in more rules should you need to. When viewing certain reports in GA4, it should look something like this when reviewing channel data.
D365 Configuration
Go ahead and add a new global Choice field called Lead Channel. This should then be used when adding new Lead Channel fields on the Lead table so the values are consistent. Make a note of the value of each label as these will be needed to update the final Lead Channel Capture script. Then use that global field to add a new Lead Channel field on the Lead table. Create a field to capture the Landing Page of the visitor which will get the first URL they land on within your website. The Referrer URL will capture the link of where they came from (unless coming direct from an Email or typing in your website address directly). These are both needed to look at the various rules to determine which Lead Channel they fall in to.
If you don’t have them already, add in the additional fields listed below to make sure you have a full well rounded solution. That way if someone landing on your site DID have UTM Parameters in the link they clicked on, you will still get that information too.
- Lead Channel – choice field including the channels to track
- Landing Page – single text
- Referrer URL – single text
- UTM Source – single text
- UTM Medium – single text
- UTM Campaign – single text
- UTM Content – single text
These are the channels I’ve added to my environment that are all included in the lead channel script provided later on.
Realtime Marketing Form
For each marketing form that you wish to use to capture the lead channel information, make sure you add all of the new fields that were added to the Lead table, and make sure they are hidden.
When adding the new form to your website, you access the script and copy from the form in Dynamics 365. It will look something like this below. Moving forwards, only add the form elements to your web page (blue part) and do not include the script (purple part). This will be added to Google Tag Manager to make sure the form loads at the right time and also captures the correct values from any UTM Parameters or Referral information.
Directly below the blue part on your web page, add the following. This will make sure that when someone visits the page with the form, the value of rtmFormExists will be pushed through as an event to Google Tag Manager and be an indicator that several scripts need to be fired to run.
<script> window.dataLayer = window.dataLayer || []; window.dataLayer.push({ 'event': 'rtmFormExists' }); </script>
Google Tag Manager Setup
This is the longest part to get set up but is where all the magic happens. Google Tag Manager will need to have several components set up for this to work. This blog assumes knowledge of GTM exists already. As with most things in GTM, setting up the components can be a little chicken and egg where something needs to be set up that references something else first. If set up in the following order, everything should be linked together correctly. Although you can use different naming logic, you may then need to edit other things later on to take that in to account.
Variables Required
The following variables need to be created in GTM:
Determine First Page View Information
Variable Details: This variable is used to return a value from the sessionStorage that will be set if this is the first page view in the current session for the person on the website. This makes sure that later on, the landing page and referral page urls only get set once and on the first page the visitor lands on.
Variable Name: RTM – First Page View
Variable Type: Custom JavaScript
Variable Script:
function() { return sessionStorage.getItem("firstHit"); }
Triggers Required
The following triggers need to be created in GTM:
First Page View
Trigger Details: Used in conjunction with the Variable and Tag of a similar name. This triggers when the First Page View variable contains the value of True (set using the first page view tag below).
Trigger Name: RTM – First Page View
Trigger Type – Page View > Window Loaded
Trigger Fires On – Some Window Loaded Events
Trigger Event – 1st drop down, First page View. 2nd drop down, equals. 3rd drop down, true.
Marketing Form Exists On Page
Trigger Details: Used to determine if the event was fired from the script added directly below the marketing form details on the website.
Trigger Name: RTM – Form Exists
Trigger Type: Custom Event
Trigger Event Name: rtmFormExists
Trigger Fires On: All Custom Events
Tags Required
The following tags need to be created in GTM:
First Page View
Tag Details: This tag sets in to the sessionStorage if this is a first page view or a subsequent visit in the session of the person on your website.
Tag Name: First Page View
Tag Type: Custom HTML
Tag Script:
<script> // check if item has been set on a previous hit if (window.sessionStorage) { if (sessionStorage.getItem("firstHit")) { sessionStorage.setItem('firstHit', 'false'); } else { // otherwise set this hit as first page view sessionStorage.setItem('firstHit', 'true'); } } </script>
Tag Firing Trigger: All Page Views
Set Current Session Information
Tag Details: This checks to see if the current referrer and landing are already stored in the sessionStorage, and if not, it stores them if present.
Tag Name: Session Info Current
Tag Type: Custom HTML
Tag Script:
<script> // Check if referrer is already stored in sessionStorage var referrer = sessionStorage.getItem("session_referrer"); var landing = sessionStorage.getItem("session_landing"); // If it's not stored, get it from document.referrer and store it if (!referrer) { referrer = document.referrer; sessionStorage.setItem("session_referrer", referrer); } // If it's not stored, get it from window.location.href and store it if (!landing) { landing = window.location.href; sessionStorage.setItem("session_landing", landing); } </script>
Tag Firing Trigger: First Page View
D365 Realtime Marketing Form Loader Script
Tag Details: This tag contains the part of the script that you removed from the original Realtime Marketing form script provided by Microsoft. It will load after the Lead Channel Capture script so that the values get loaded first from the Referral and Landing Page, and are populated into the values on the form. This script is used to actually load the form.
Tag Name: RTM – D365 RTM Form Loader
Tag Type: Custom HTML
Tag Script:
<script src='https://cxppusa1formui01cdnsa01-endpoint.azureedge.net/global/FormLoader/FormLoader.bundle.js'></script>
Tag Firing Trigger: None
Lead Channel Capture Script
Tag Details: This is the main script that is running to extract all of the correct information from the sessionStorage to get the original and/or current referral, utm and lead channel information and add it to the hidden fields on your RTM form so they are added to the Lead created.
Tag Name: RTM – Lead Channel Capture
Tag Type: Custom HTML
Tag Extra Info: Select Support document.write
Tag Advanced Settings: Tag firing options should be Once per event
Tag Sequencing: Fire a tag before RTM Lead Channel Capture fires then select Session Info Current. This makes sure the Session Info is populated prior to the Lead Channel Capture script running so it can access the URL information needed.
Tag Firing Trigger – RTM – Form Exists
Tag Script:
<script> // Define the findInputByLabelText function in the global scope function findInputByLabelText(labelText) { var labels = document.querySelectorAll('label[title="' + labelText + '"]'); for (var i = 0; i < labels.length; i++) { var label = labels[i]; var inputId = label.getAttribute("for"); if (inputId) { var inputElement = document.getElementById(inputId); if (inputElement) { return inputElement; } } } return null; } function processRulesAndPopulateFields(sessionLanding) { // Define recognized social networks, social media sites, and search engines var recognizedSocialNetworks = ["facebook", "twitter", "instagram", "youtube", "linkedin","linktree"]; var socialMediaSites = ["facebook.com", "twitter.com", "instagram.com", "linkedin.com","lnkd.in","t.co"]; var searchEngines = ["google", "bing", "yahoo","duckduckgo"]; var excludeWords = ["email", "adwords", "ppc", "paidsearch", "paidsocial", "cpc"]; // Retrieve values from sessionStorage and decode them var sessionLandingEncoded = sessionStorage.getItem("session_landing"); var sessionLanding = decodeURIComponent(sessionLandingEncoded); // Extracting information from the sessionLanding value var sessionParams = new URL(sessionLanding); var sessionUtmSource = sessionParams.searchParams.get('utm_source'); var sessionUtmMedium = sessionParams.searchParams.get('utm_medium'); var sessionUtmCampaign = sessionParams.searchParams.get('utm_campaign'); var sessionUtmContent = sessionParams.searchParams.get('utm_content'); // Get the referrer values from sessionStorage var sessionReferrer = sessionStorage.getItem('session_referrer'); // Check if referrer values are not empty and are valid URLs var sessionDomain = getDomainFromURL(sessionReferrer); function getDomainFromURL(urlString) { try { // Add a default protocol if missing var urlWithProtocol = urlString.startsWith('http') ? urlString : 'https://' + urlString; // Return the hostname if the URL is valid return new URL(urlWithProtocol).hostname; } catch (error) { // Log the error if the URL is invalid console.error('Error parsing URL:', urlString, error.message); return 'N/A'; } } // Find the right fields var referrerUrlField = findInputByLabelText("Referrer URL"); var sessionLandingField = findInputByLabelText("Last Landing Page"); var utmSourceField = findInputByLabelText("UTM Source"); var utmCampaignField = findInputByLabelText("UTM Campaign"); var utmMediumField = findInputByLabelText("UTM Medium"); var utmContentField = findInputByLabelText("UTM Content"); // Populate the form fields with correct values if (referrerUrlField) referrerUrlField.value = sessionReferrer || "N/A"; if (sessionLandingField) sessionLandingField.value = sessionLanding || "N/A"; if (utmSourceField) utmSourceField.value = sessionUtmSource || "N/A"; if (utmCampaignField) utmCampaignField.value = sessionUtmCampaign || "N/A"; if (utmMediumField) utmMediumField.value = sessionUtmMedium || "N/A"; if (utmContentField) utmContentField.value = sessionUtmContent || "N/A"; // Lead Channel Rules // Default rule numbers var defaultRuleNumber = 916780011; var sessionRuleNumber = defaultRuleNumber; // Initialize flag variables var sessionRuleMatched = false; // Rule 1: Organic social if (!sessionRuleMatched && ( (sessionUtmMedium === "social" && recognizedSocialNetworks.includes(sessionUtmSource)) || (sessionUtmMedium === "social" && socialMediaSites.some(function(site) { return sessionDomain.includes(site); })) ) ) { sessionRuleNumber = 916780007; sessionRuleMatched = true; } // Rule 2: Email marketing if (!sessionRuleMatched && sessionUtmMedium === "email") { sessionRuleNumber = 916780001; sessionRuleMatched = true; } // Rule 3: SMS marketing if ( !sessionRuleMatched && ( sessionUtmSource === "sms" || sessionUtmMedium === "sms" ) ) { sessionRuleNumber = 916780001; sessionRuleMatched = true; } // Rule 4: Paid social if (!sessionRuleMatched && ((sessionUtmMedium && (sessionUtmMedium === "paid" || sessionUtmMedium === "ppc" || sessionUtmMedium === "cpc")) && (sessionUtmSource && (recognizedSocialNetworks.includes(sessionUtmSource) || socialMediaSites.includes(sessionDomain)))) ) { sessionRuleNumber = 916780002; sessionRuleMatched = true; } // Rule 5: Paid search if (!sessionRuleMatched && ( (sessionUtmSource && sessionUtmSource.includes("paidsearch")) || (sessionUtmMedium && sessionUtmMedium.includes("adwords")) || (sessionUtmMedium && sessionUtmMedium.includes("ppc")) || (sessionUtmCampaign && sessionUtmCampaign.includes("adwords")) || sessionParams.searchParams.has("gclid") || ((sessionParams.searchParams.has("utm_source") || sessionParams.searchParams.has("utm_medium") || sessionParams.searchParams.has("utm_campaign")) && searchEngines.includes(sessionDomain)) ) ) { sessionRuleNumber = 916780003; sessionRuleMatched = true; // Add this line to mark the rule as matched } // Rule 6: Display if (!sessionRuleMatched && sessionUtmMedium === "display") { sessionRuleNumber = 916780004; sessionRuleMatched = true; } // Rule 7: Affiliates if (!sessionRuleMatched && sessionUtmMedium && (sessionUtmMedium.includes('affiliate') || sessionUtmMedium.includes('affiliates'))) { sessionRuleNumber = 916780005; sessionRuleMatched = true; } // Rule 8: Other campaigns var sessionSourceParams = [sessionUtmSource, sessionUtmMedium, sessionUtmCampaign, sessionParams.searchParams.get('source')]; var sessionContainsExcludedWords = excludeWords.some(function (word) { return sessionSourceParams.some(function (param) { return param && param.toLowerCase().includes(word); }); }); if (!sessionRuleMatched && sessionContainsExcludedWords) { sessionRuleNumber = 916780006; sessionRuleMatched = true; } // Rule 9: Organic search if (!sessionRuleMatched && searchEngines.some(function(engine) { return sessionDomain.includes(engine); })) { sessionRuleNumber = 916780008; sessionRuleMatched = true; } // Rule 10: Referral if (!sessionRuleMatched && !socialMediaSites.includes(sessionDomain) && !searchEngines.includes(sessionDomain) && sessionDomain !== 'N/A') { sessionRuleNumber = 916780009; sessionRuleMatched = true; } // Rule 11: Direct if (!sessionRuleMatched && sessionDomain === 'N/A') { sessionRuleNumber = 916780010; } // Return an object containing session rule numbers return { sessionChannel: sessionRuleNumber, }; } // Results document.addEventListener("d365mkt-afterformload", function () { var rulesResult = processRulesAndPopulateFields(sessionStorage.getItem('session_landing')); console.log("d365mkt-afterformload", "Session Channel:", rulesResult.sessionChannel); // Populate Session Lead Channel var sessionLeadChannelField = findInputByLabelText("Lead Channel"); if (sessionLeadChannelField) { sessionLeadChannelField.value = rulesResult.sessionChannel; } }); </script>
Final Steps
After adding all of the variables, triggers and tags to Google Tag Manager, preview your changes to make sure everything is working as expected. If set up correctly, when reviewing in the GTM preview, you should see that the rtmFormExists event fires after the Container Loaded built in trigger. On that step, you should see that the two tags fired for RTM – Lead Channel Capture and then RTM – D365 RTM Form Loader. Fill out your form and then review the new Lead in D365 to make sure your hidden fields were populated.
Once working, Publish the changes in Google Tag Manager.
Each time a new RTM Form needs to be added to your website, be sure to remove the bottom script part below, and do not include it along with the rest of the details.
<script src=’https://cxppusa1formui01cdnsa01-endpoint.azureedge.net/global/FormLoader/FormLoader.bundle.js’></script>
Then make sure to add this script below the remaining form code on your webpage:
<script> window.dataLayer = window.dataLayer || []; window.dataLayer.push({ 'event': 'rtmFormExists' }); </script>
Here we can see all of the fields captured on a Lead in D365, where this one did include UTM parameters, and all of the rules were checked until it hit a match for the Lead Channel of Referral. In this instance, I could add linktree to the list of social networks in the Lead Channel, then moving forward, it would be flagged as Organic Social instead.
In this instance, there were no UTM Parameters in the landing page URL, but the referrer of Google was found in the list of search engines, so the Lead Channel was set as Organic search.
When the person closes their browser, this will clear the sessionStorage, so the process will start again next time they visit your website. Hope this helps!
Check out the latest post:
Split Your Audience By Number Or Percentage In Customer Insights - Journeys
This is just 1 of 480 articles. You can browse through all of them by going to the main blog page, or navigate through different categories to find more content you are interested in. You can also subscribe and get new blog posts emailed to you directly.
Hi Megan,
I have to enter “https://cxppusa1formui01cdnsa01-endpoint.azureedge.net” as approved script source in the security headers for it to run. Are we vulnerable for attacks where people can run scripts from that site if they have access to publish content on our website?
Thank you!
Hi Marie, not quite sure I understand the question fully. The part where you have said ‘people can run scripts from that site if they have access to publish content on your website’. Are you saying you have given access to 3rd party agencies and worried they can add scripts to your site, or something different?