This script is a precautionary backup for your Google Ads tracking template. If the UTM template on a campaign, ad group, or ad ever gets cleared or fails to apply, this script re-applies it automatically so click and attribution data keeps flowing into Google Analytics instead of going untracked.
It works entirely inside your Google Ads account. You do not need to touch Aesthetix CRM to set it up, but it protects the tracking data that feeds your Google Ads Reporting inside Reporting and your campaign performance inside Ads Manager > Ad Reporting.
The script scans your enabled campaigns and ad groups and rebuilds a tracking template using values like the campaign name, ad group name, ad name, keyword, and match type. It writes that template back onto each ad group so every click carries the correct UTM parameters (utm_source, utm_medium, utm_campaign, utm_content, and so on) back to Google Analytics.
Think of it as insurance: your primary UTM tracking template should already be doing this job, but if it ever gets overwritten, disabled, or fails silently, this script acts as a fail-safe that keeps reapplying the correct tracking parameters on a schedule.
The only value in the script you need to update is the landing page URL variable ({lpurl}), which Google Ads fills in automatically, so in most cases you will not need to change anything beyond confirming the parameters match the UTM structure your campaigns already use.
Log in to the Google Ads account connected to your practice's ad campaigns.
Click Tools and Settings, then go to Bulk Actions > Scripts.

If you do not see Scripts under Bulk Actions, switch to the advanced view of your account. The Scripts tool is only available there.
Create a new script, remove any placeholder code Google Ads adds by default, and paste in the script below:
function main() {
var TrackingTemplate = "{lpurl}?utm_source=adwords&utm_medium={AdName}&utm_campaign={CampaignName}&utm_content={AdGroupName}&utm_keyword={keyword}&utm_matchtype={matchtype}&campaign_id={campaignid}&ad_group_id={adgroupid}&ad_id={creative}";
var _CAMPAIGN_CONTAINS = "";
var _ADGROUP_CONTAINS = "";
var STATUS = "ENABLED";
if (TrackingTemplate.search(/{AdGroupName}|{CampaignName}|{AdName}/g) == -1) {
Logger.log("Enter at least one of the {CampaignName} or {AdGroupName} or {AdName} parameter in the tracking template");
return
}
if (TrackingTemplate.search("{AdGroupName}") > 0) {
var adgroupIterator = {
hasNext: function() {
return false
}
}
if (_ADGROUP_CONTAINS == "" && _CAMPAIGN_CONTAINS == "") {
adgroupIterator = AdsApp.adGroups().withCondition("Status = " + STATUS).get();
} else if (_ADGROUP_CONTAINS == "" && _CAMPAIGN_CONTAINS !== "") {
adgroupIterator = AdsApp.adGroups().withCondition("CampaignName contains '" + _CAMPAIGN_CONTAINS + "'").withCondition("Status = " + STATUS).get();
} else if (_ADGROUP_CONTAINS !== "" && _CAMPAIGN_CONTAINS !== "") {
adgroupIterator = AdsApp.adGroups().withCondition("CampaignName contains '" + _CAMPAIGN_CONTAINS + "'").withCondition("Name contains '" + _ADGROUP_CONTAINS + "'").withCondition("Status = " + STATUS).get();
} else if (_ADGROUP_CONTAINS !== "" && _CAMPAIGN_CONTAINS == "") {
adgroupIterator = AdsApp.adGroups().withCondition("Name contains '" + _ADGROUP_CONTAINS + "'").withCondition("Status = " + STATUS).get();
}
if (!adgroupIterator.hasNext()) {
Logger.log("No Campaigns/Adgroups matched with this condition");
return
}
while (adgroupIterator.hasNext()) {
var adgroup = adgroupIterator.next();
var adgrouptemplate = TrackingTemplate.replace(/{AdGroupName}/g, adgroup.getName().replace(/\s/g, '%20'))
if (TrackingTemplate.search("{CampaignName}") > 0) {
adgrouptemplate = adgrouptemplate.replace(/{CampaignName}/g, adgroup.getCampaign().getName().replace(/\s/g, '%20'))
}
if (TrackingTemplate.search("{AdName}") > 0) {
var adsIterator = adgroup.ads().get();
while (adsIterator.hasNext()) {
var ad = adsIterator.next();
var adType = ad.getType();
var headline = "";
if (ad.getHeadline()) {
headline = ad.getHeadline();
} else if(ad.isType().expandedTextAd()) {
headline = ad.asType().expandedTextAd().getHeadlinePart1();
} else if(ad.isType().gmailImageAd()) {
headline = ad.asType().gmailImageAd().getName();
} else if(ad.isType().gmailMultiProductAd()) {
headline = ad.asType().gmailMultiProductAd().getHeadline();
} else if(ad.isType().gmailSinglePromotionAd()) {
headline = ad.asType().gmailSinglePromotionAd().getHeadline();
} else if(ad.isType().html5Ad()) {
headline = ad.asType().html5Ad().getName();
} else if(ad.isType().imageAd()) {
headline = ad.asType().imageAd().getName();
} else if(ad.isType().responsiveDisplayAd()) {
headline = ad.asType().responsiveDisplayAd().getShortHeadline();
} else if(ad.isType().responsiveSearchAd()) {
var headlines = ad.asType().responsiveSearchAd().getHeadlines();
if (headlines && headlines[0].text) {
headline = headlines[0].text;
}
}
Logger.log("Headline text : " + headline);
if (headline) {
adgrouptemplate = adgrouptemplate.replace(/{AdName}/g, headline.replace(/\s/g, '%20'))
} else {
adgrouptemplate = adgrouptemplate.replace(/{AdName}/g, ad.getId())
}
}
}
adgroup.urls().setTrackingTemplate(adgrouptemplate);
Logger.log(adgroup.getCampaign().getName() + " => " + adgroup.getName() + " => " + adgrouptemplate)
}
}
}Close out of the script editor, give the script a clear name (for example "UTM Tracking Fail-Safe"), and change its frequency from the default to Hourly. Running it hourly keeps your tracking template refreshed throughout the day without you needing to check on it manually.

Before the script runs on its own schedule, Google Ads will ask you to authorize it and run a preview. Confirm the preview shows the majority of your campaigns coming back as successful with a landing page found before you let it run automatically.
Will this script affect other scripts I already have running? Yes. Because this script writes a tracking template onto your ad groups, it can override tracking templates set by other scripts if they touch the same UTM parameters. Review any other scripts running on the account before turning this one on so they do not conflict.
Why does Google Ads ask me to authorize the script? Google Ads requires authorization anytime a script is going to make changes inside your account, in this case updating tracking templates on ad groups. Always review the preview run first: it should show most campaigns as successful with a matching landing page found before you approve it.
Do I need to edit the script before using it?
In most cases, no. The {lpurl} value is filled in automatically by Google Ads based on your ad's landing page, and the other parameters (campaign name, ad group name, ad name, keyword, match type) are pulled directly from your account. You only need to adjust the tracking template string if your practice uses a different UTM structure than the one shown here.
What happens if I do not set this up? Your campaigns will still track through your primary UTM template as long as it stays intact. This script only matters as a backup: if the primary template gets cleared or corrupted, clicks could stop carrying UTM data until someone notices and fixes it manually. The script closes that gap automatically.
Does this replace the reporting inside Aesthetix CRM? No. This script only protects the tracking parameters at the Google Ads level. Once clicks carry the correct UTM data, you can still review campaign performance in Ads Manager > Ad Reporting and cross-channel attribution in Reporting.
How often should the script run? Hourly is recommended. It is frequent enough to catch a broken tracking template quickly without creating unnecessary load on your account.
Can I limit the script to specific campaigns or ad groups?
Yes. The script includes optional filters (_CAMPAIGN_CONTAINS and _ADGROUP_CONTAINS) where you can enter partial campaign or ad group names. Leave them blank to apply the tracking template account-wide, or fill them in to target only matching campaigns or ad groups.
Is this required to use Google Ads Reporting in Aesthetix CRM? No. It is an optional safeguard for practices that want extra protection against broken UTM tracking. Your Google Ads Reporting will work as long as your campaigns are sending properly tagged click data, whether that comes from your primary UTM template alone or with this script backing it up.