Best Practices for UTM Processing from Website > Marketo > CRM

Level 4 - Champion Level 4 - Champion
Level 4 - Champion

UTMs are a really valuable tool when it comes to understanding where your traffic is coming from, but not processing their data properly can get in the way of making them an effective data set. 


In this post, I’m sharing some best practices for retaining your UTM data in Marketo and sharing that data with your CRM (namely Salesforce). Take a look.

First: a reminder on UTMs

Before you embark on your UTM journey, it’s important to make sure your team is using UTMs in a consistent and accurate way. On the one hand, this means ensuring you’re using UTM codes for anything you publish on your channels. On the other hand, it means creating consistent parameters and naming conventions, down to the case. If you change how you’ve named something from one campaign to the next, then your data isn’t going to group properly. 

Basic source tracking in Marketo

In Marketo’s native instance, UTMs are captured within hidden fields that live on a form that’s filled out by someone interested in your webinar, gated content, etc. 


In this approach, you also need a process for consistently updating your forms to add those hidden fields, ensure they’re written properly, and confirm they’re pulling from the parameter correctly. This native process then stores the value in the field you’re leveraging and typically overwrites whatever the last parameter UTM or details were.

An alternative for capturing UTMs

An option to improve your UTM tracking is to put a tracking code on the website, which inserts the UTM values and the rest of the field values directly without you needing to hide the fields on the form. 


There are two benefits to this approach: 

  1. It’s automated. If you are using more than one form, you don’t have to modify those hidden fields on each individual form. So, if you change how you’re doing things, you don’t have to monitor that the hidden UTM fields are being added correctly. Instead, you just have to update the script, make sure the fields exist in the system, and confirm that you have the right API name for them.
  2. It persists the values across multiple pages. In Marketo’s native functionality, the form fill function only tracks the UTM if the form lives on the inbound landing page (e.g. if someone has clicked on the link in LinkedIn and filled out the form immediately without going to another page first). With the tracking script, the values persist across multiple page visits in order to capture them when the form is actually filled out. 

The script also allows you to capture multiple field sets. If you wanted to have a set of fields that include first and original UTMs, for instance, you would be able to capture the current set and then a set that was prior to them filling out a form. This makes it more informative as you look to understand what’s driving people to your brand. 


For those with compliant Google Analytics set ups, it’s possible, along with this tracking code, to also layer in data from your Analytics platform where UTMs aren’t present. This means that we’re able to see other data that would never have UTMs against it to understand how people are coming in (e.g. someone finds a webinar via organic search). For marketers that aren’t very consistent in their use of UTMs, this can also create some additional redundancy. 

Processing UTMs within Marketo

As you’re setting up your UTM processor on the Marketing Activities page in Marketo, it’s key to think about your UTMs in a multi-faceted way. Many people will set up a single set of UTMs, but that can lead to issues where UTMs are used inconsistently as a group. Instead, thinking about them as groups, rather than individual fields is key to ensuring you have a clean data architecture. 


To that end, what I recommend doing is maintaining four sets of UTM fields. Now, I know that might sound excessive, but bear with me. 

The 4 UTM field sets

The first set should be “temp” UTM fields. This would include one temporary holding field for each of the parameters you’re capturing, and would be used in your hidden fields or your API script to capture UTMs. These fields are then used to push that data into your more permanent holding data set values, after which they are nulled. 


The key here is that they remain empty 99.9% of the time as they are used only for capture, not retention. By separating the caption function from the retention function, you automatically create a more maintained data set as anything that’s empty allows you to null it later. If the field is empty, then the most recent field that correlates with that should also become empty or nulled.  


The second set of fields is your “most recent” or “current” UTM set, which holds the data most recently pushed from the temp set. And the third set is the “first/original” UTM set, which you may choose not to have. 


The fourth set that I recommend leverages the Program Member field functionality that was introduced in Marketo last year. As the name implies, Program Member fields are specific to that unique program, allowing you to push data to and retain data on a specific program within Marketo. 


In other words, if you’re hosting a webinar, you have your global form and it captures the temp UTMs. If you push that data into the “most recent” field, that will be retained until new data comes in to replace it. Meanwhile, if you push it to the “program” fields, you can see what drove people to that particular webinar in perpetuity. This ensures that you have a local data set of performance analysis for that particular webinar. 



The one thing you do have to be careful with is that you’re only allowed a total of 20 Program Member fields for this system, and you’d be automatically dedicating five to 10 fields to this use case. It may feel like a big investment, but you’d be using the function for what it was primarily designed for. 

Sharing your Marketo UTM data with Salesforce

The best practice for any Marketo program is to also have a linked SFDC campaign with which it can maintain a one-to-one reporting relationship on status and membership. 


Prior to Marketo introducing the Program Member feature, the only way to look at a program by program (or campaign by campaign) process was to do it at the Campaign Member object inside of Salesforce. This meant pushing the data into the Lead/Contact object inside of Salesforce, and then using that to parse or process them onto the Campaign Member object in Salesforce via a nightly batch or triggered process.


For anyone still using this approach, I highly recommend that you continue to leverage your “temp” fields to drag that data, rather than your “most recent”. 


I also recommend that you leverage Salesforce to null that data, rather than doing that inside of Marketo. This is because Marketo doesn’t have transparency as to when the data has or hasn’t been written inside of Salesforce, which can pose an issue if your data is nulled before it’s written.

SFDC trigger campaign member sync

The processes below both require Marketo to sync the Program Member over and sync the person to Salesforce so that the data can be acted on. 


The challenge with this approach is that it can cause potential delays and potentially create data on the wrong campaign. For example, with the nightly batch approach, if someone registers for your webinar with UTMs and then registers for three more webinars, that fourth webinar is going to match the most recent campaign data that you have on that person. The UTMs would be written against that fourth webinar, rather than the trigger event for the other registrations. 


As you consider this approach, it’s helpful to know what your ability to modify things inside of Salesforce is and how open your Salesforce admin is to allowing these types of developments. That said, they’re not going to require a heavy load on the system.


Because of the nature of Marketo, Program Member data does not write to Campaign Member data. So, in order to make this work, you have to write the data to the Person object inside Marketo and then translate it to the Lead/Contact object in Salesforce. That’s then pushed to the associated Campaign Member and nulled. To get this right, the name on your SFDC campaign must be an exact match to the naming parlance in Marketo, otherwise, you might break the setup. 

Custom SFDC webhook campaign member sync

The alternative to the processes above is to work outside of the native Marketo/Salesforce integration. You can do this with a third-party tool like Workato or where you pass the Marketo campaign information into Salesforce via that software. Or you can build your own custom webhook. 


The webhook is built on the Marketo side, and then the SFDC admin would have to surface a public REST API input on the Salesforce side. This public REST site is only going to point to the Campaign Member object—it won’t modify or update data related to the account, contact, lead, or opportunity. As such, the risks are relatively low, but they will still warrant a conversation with your SFDC admin. 


A webhook build like this one removes the potential issue of binge behavior. Each call that’s made to push that data across is a packet of data that’s generated at that moment and then doesn’t change. So, if someone came in and binged four separate webinars, a separate packet for each of those four engagements would be pushed.

What this looks like in practice 

Nulling data 

When you have a data setup where you’re leveraging your Program Member fields, and using an API connection to transfer that data, you can revert to nulling that information on the Marketo side instead of relying on Salesforce. 


In this process, we request this campaign via the local Program process that is translating that data into the Program Member fields. Then, we push the webhook that’s going to tell us the timing is right for us to write the information into our Person or Contact/Lead fields and then null them. 


In the flow, start by writing the Person field in Marketo that correlates to the Contact/Lead on the Salesforce side, using tokens based on the “temp” values for each data point. 


Then set a short waiting period. Note that your system may take a bit longer than two seconds. 


Then null those values. 


If we were also writing to original fields using this process, we could insert a “request campaign” or “remove from flow” step prior to nulling these values, or have them nulled in a subsequent process. 


Writing the original fields during that flow step would be a great use of the Execute Campaign process, in order to ensure that each of those flow steps was executed prior to requesting the null campaign. This step can also be pulled into the process. 

Writing in the Program Member fields

In this case, the trigger is that our program status is changed.


The first step is to update people via a webhook, including an exception for people who have already been inserted. 


Then the people that are on the list are removed from the flow. 


We write a number of fields into this specific program—some of which are non-UTM fields. For example, the Program Actual Engagement Date is useful because Salesforce writes the campaign member engagement date based on when the person is added to the program. But if there’s a delay in the processing, or if you’re adding people from a list from an event, that could be off. 


For program UTM fields, we’re using the temp fields to write in on each of them. Next, we run the webhook to insert the data into Salesforce and request the campaign to write those fields against our most recent fields. 


Note that this second webhook is used to insert Person data instead of updating it, which is the function of the first webhook.


Once the program is live and we go in to look at our members, we can modify our view to only show data that’s relevant to UTM. This will be mirrored by what’s reflected in the SFDC campaign if your transfer of information is successful. 


If you don’t have the time or resources to build a webhook, this data would give you the right insight to understand what drove people to a particular event. 

Level 10 - Community Moderator

Change Data Value is synchronous. So there isn’t a need for a Wait step there.

Level 8 - Champion

@Andy_Caron3 this is really awesome, and a great overview for capturing UTMs.  I am a really big fan of using Program Member fields to capture conversion UTMs at the program level, and really liked how you are capturing additional source data through your examples.  

Level 10 - Community Advisor + Adobe Champion

@Andy_Caron3 this is a great article. Love the concept of the temp fields, as it solves a few headaches alright.

Marketo Employee

Awesome article! 

Level 2

Great Article!

Level 1

Were there links to the utm scripts/tracking code in this post at some point? I'm looking forward to setting this up, but would like to use the same scripts mentioned above if still available. 

Level 10 - Community Moderator

Were there links to the utm scripts/tracking code in this post at some point? 

Don’t believe so. There are several possible JS libraries to use; community rules prohibit directly endorsing one over another since the author usually provides consulting services as well.


The good news I’ll be debuting a new and very robust tracking JS (microTracker) in a month or so and hosting it here on the Products blog (so it doesn’t fall outside the community).

Level 1

Great overview with specific use cases and solutions. Thanks for sharing the knowledge!

Level 2

Can we take "Original referrer" url to decipher the first touch utm values? 
And @SanfordWhiteman any update on your UTM tracking system please?

Level 9 - Community Advisor

Great article! Thanks for sharing