Last updated 1 December 2023: New consent signals added

Ever since Google Analytics released the first features of consent mode, I’ve been anxiously waiting for news about a more tightly-knit integration with Google’s preferred implementation solution: Google Tag Manager.

In a recent release, Google released a veritable cornucopia of new features that should assist in determining and implementing consent not just across Google tags, but any tags running in the container.

The new features are:

  • New consent types (in addition to ad_storage and analytics_storage which were introduced with consent mode).
  • Tag-specific consent settings with both built-in consent checks (if their behavior is modified by consent state) and additional consent checks, where you can determine if a tag requires specific types of consent to be allowed to fire in general.
  • A new Consent Overview screen, where you can quickly check which tags have already been configured for additional consent settings and which are still waiting to be updated.
  • New trigger types, with both a Consent Initialization trigger (fires before anything else in the container) and an Initialization trigger (fires before anything except the Consent Initialization trigger) being added to the mix.
  • New custom template sandbox APIs for making consent management platform templates (and why not others as well) consent-aware.

These features are available in both GTM 360 and regular (free) GTM containers.

Let’s take a closer look at all of these.


The Simmer Newsletter

Subscribe to the Simmer newsletter to get the latest news and content from Simo Ahava into your email inbox!

First of all, in addition to ad_storage and analytics_storage, which were introduced with consent mode, there are five additional consent types Google recommends to use with these APIs. Here’s the full list (source):

Consent type Required for Google’s services Description
ad_storage Yes Enables storage (such as cookies) related to advertising
analytics_storage Yes Enables storage (such as cookies) related to analytics e.g. visit duration
ad_user_data Yes Whether Google’s services can use user data for building advertising audiences
ad_personalization Yes Whether Google’s services can use the data for remarketing
functionality_storage No Enables storage that supports the functionality of the website or app e.g. language settings
personalization_storage No Enables storage related to personalization e.g. video recommendations
security_storage No Enables storage related to security such as authentication functionality, fraud prevention, and other user protection

Note! The last three consent types are only for non-Google tags. Google tags (e.g. Analytics, Ads, and Floodlight) are only sensitive to ad_storage, analytics_storage. ad_user_data, and ad_personalization.

It’s somewhat odd seeing functionality_storage and security_storage here, as at least in the European Union they are typically deemed to be strictly necessary storage and thus wouldn’t require consent at all. On the other hand, having more configuration options is rarely a bad thing.

You can actually add any consent types you like into the respective API calls and tag settings. The three additional consent types are Google’s recommendations, but they’re not an exhaustive list.

If you open a tag, any tag, and scroll down to its Advanced Settings, you’ll find a section titled Consent Settings.

There are two sections here (although you might see just one in some cases).

Built-in Consent Checks means that the tag (or, more precisely, the template running the tag) is consent-aware. This means that the tag is doing something with consent state, although it isn’t really explained what it is.

In the screenshot above, for example, you can see ad_storage and functionality_storage listed in the Built-in Consent Checks section. This means that this particular tag template has defined in its permissions that it needs read and/or write access to these particular consent types.

It’s a good thing to be aware of, in case you are wondering why the tag isn’t behaving as you’d expect it to behave.

Google tags (Analytics, Ads, and Floodlight) have built-in checks for analytics_storage, ad_storage, ad_user_data, and ad_personalization, and their behavior is documented. Other tags might show other consent types here, and it’s up to these tags’ documentation to explain how they are accessing consent state.

Note! Built-in Consent Checks is only visible if the template is accessing consent state in its code. If it has nothing to do with consent APIs, you won’t see Built-in Consent Checks under these settings.

The other section is called Additional Consent Checks, and…

This. Is. Really. Cool.

With this setting, you can essentially handle consent checks without having to apply consent check conditions to the tag’s triggers!

There are three options here:

  • Not set – the default option. Use this to indicate this tag hasn’t been properly reviewed for whether its triggering should be conditioned on consent or not.
  • No additional consent required – Set this if the tag’s triggering should not be limited based on consent.
  • Require additional consent for this tag – Set this if the tag should only fire if consent has been granted to one or more consent types. When you check this option, a small table appears, and you can add any of the available consent types into the table. The tag will only fire if all the listed consent types have the granted state.

The tag with the settings as above will only fire if security_storage, functionality_storage, and analytics_storage have been granted consent.

To enable the Consent Overview screen, first go to Admin and then Container Settings.

Check the box next to Enable consent overview.

Now when you go to Tags, you can click the Consent Overview icon in the action bar.

Consent Overview lists all your tags in two sections.

Consent Not Configured means that the Additional Consent Checks setting for these tags is the default Not set.

Consent Configured means that the tag has been configured for additional consent checks (either None or one or more consent types that need to be granted for the tag to fire).

You can mass edit the consent check settings for tags by selecting them and then clicking the Edit Consent Settings button in the action bar.

Use the Consent Overview screen religiously, especially if you are integrating Google Tag Manager with a consent management platform (CMP). It’s a great way to quickly see how and if your tags have been configured to work with the consent settings set by your CMP.

New trigger types

There are two new trigger types in Google Tag Manager. Both are also automatically added to each container as built-in triggers, and both appear in Preview mode as new trigger events.

Note! These triggers can’t really handle asynchronous requests. If you have an asynchronous call to fetch consent state, for example, then it’s possible that even when firing the tag with the Consent Initialization trigger, it completes only after some other tags have already started firing on later triggers.

Consent Initialization is a trigger that fires before any other trigger in the container. It is programmatically generated to be the very first trigger that can fire on any given page.

This means that it’s good for, as the name implies, establishing the default consent state of the new consent types, for example (see below).

In other words, if you are using a consent management platform and you want to run some code (perhaps their own custom template) that establishes the default consent state for the seven different consent types, this is the correct trigger type to use.

The built-in trigger that’s automatically available in all containers is called Consent Initialization - All Pages.

The dataLayer event for this trigger type is gtm.init_consent (although you won’t actually find it in the dataLayer array), and in Preview mode it shows up as Consent Initialization.


The other new trigger type is Initialization. This fires after Consent Initialization but before the first trigger events that were actually pushed into dataLayer.

It’s where you’d establish non-consent-related dependencies for the rest of the container to make use of.

As it fires before anything else that’s pushed into dataLayer, it guarantees that tag execution begins before any trigger based on actual dataLayer events has a chance to fire. Just note the caveat about asynchronous tags above!

The built-in trigger is called Initialization – All Pages.

The dataLayer event for this trigger type is gtm.init, and in Preview mode it shows up as Initialization.

New template APIs

If you’re not looking to build custom templates, you can skip to the next chapter.

If you’re building a custom template for a consent management platform (or really any script that should define consent status), there are new template APIs available for you.

There are also some new APIs available for making templates generally sensitive to consent state on the site.

Default consent state is something that’s applied as soon as the tag fires (preferably on the Consent Initialization trigger). This establishes a baseline for consent settings. Its values can be derived from CMP-specific cookies or something in the dataLayer, for example.

The API is called setDefaultConsentState, and it takes an object with five properties, each corresponding to a consent type listed in the table above. You can also provide an optional region parameter to have the settings established in the API call apply to visitors from specific regions. The regions (and sub-regions) are provided in ISO 3166-2 format.

The default settings are applied until consent state is updated.

In the example below, the CMP template establishes a default state of denied across the board, except for USA, where all but ad_storage, ad_user_data, and ad_personalization are set to granted state.

const setDefaultConsentState = require('setDefaultConsentState');

  ad_storage: 'denied',
  analytics_storage: 'denied',
  ad_user_data: 'denied',
  ad_personalizatio: 'denied',
  functionality_storage: 'denied',
  personalization_storage: 'denied',
  security_storage: 'denied'

  analytics_storage: 'granted',
  functionality_storage: 'granted',
  personalization_storage: 'granted',
  security_storage: 'granted',
  region: ['US']

Naturally, you’ll want to make the values dynamic based on, for example, existing consent cookies or settings in the dataLayer.

If the user then interacts with the consent management platform and changes their preferences, you can have the template call the updateConsentState API with a similar structure as above.

Note that region is not available in updateConsentState. That’s because when you need to invoke this API you should already know what the state of the consent call should be, regardless of where the user is located.

const updateConsentState = require('updateConsentState');

  ad_storage: data.ad_storage,
  analytics_storage: data.analytics_storage,
  ad_user_data: data.ad_user_data,
  ad_personalization: data.ad_personalization,
  functionality_storage: 'granted',
  personalization_storage: data.personalization_storage,
  security_storage: 'granted'

In the example above, functionality and security storage are hard-coded to granted, and all the other consent states are pulled in from template fields, most likely configured to respond to user choice.

If you want a template to change its behavior based on the current consent state, you can use the isConsentGranted API.

This takes a single parameter – one of the five consent types – and returns true or false, depending on what the state of consent is for the given consent type.

For example, to set a cookie only if analytics_storage consent has been granted, you can run this code:

const isConsentGranted = require('isConsentGranted');

if (isConsentGranted('analytics_storage')) {
  // Custom method that handles setting the cookie

Sometimes it’s super clumsy to just add triggers to a tag to make it re-evaluate its functionality based on consent settings, for example. That’s why there’s a new API, addConsentListener, which can be used to execute a callback in a template once the consent state for any given consent type changes.

const addConsentListener = require('addConsentListener');

const consentCallback = (consentType, granted) => {
  if (consentType === 'analytics_storage' && granted) {

addConsentListener('analytics_storage', consentCallback);

The callback is passed the consent type and whether consent was granted or not (true / false) as arguments.

This is a pretty nifty solution, as it leaves the batching and dispatching logic to the tag itself. You don’t need to overengineer the tag with custom triggers that detect the change in consent state – the template will handle this for you.

The template will simply listen for changes to consent state, and automatically send the hit that was blocked due to lack of consent before the state update.

There is also the new Accesses consent state permission, which controls which consent states the template is allowed to read/write.

Here are the permissions required by all of the APIs listed above:

API name Requires read Requires write
setDefaultConsentState No Yes
updateConsentState No Yes
isConsentGranted Yes No
addConsentListener Yes No

Each consent type that your APIs want to read and/or write needs to be separately listed in the permissions table (thus it can have a maximum of five rows).

Any consent types you add to this permission table will be listed in the Built-in Consent Checks section of the tag.

It’s no secret that these new consent settings have been created specifically with CMPs in mind.

However, for there to be an actual benefit to using a CMP with these new settings, a couple of things need to be in place:

  1. The CMP needs to build a custom template that makes use of the new consent types and of both the default and update consent status APIs.
  2. Ideally, tags you run in the container would modify their behavior based on consent state. They can do this with the new template APIs that let templates react to consent state (and changes).
  3. At the very least, you should go through every tag in the container and check its Advanced Settings to see if consent status should block the tag if not granted.

The biggest bottlenecks are obviously (1) and (2). Judging by the list provided by Google, it doesn’t look like the most popular CMPs are exactly jumping at the opportunity to integrate with either GTM or consent mode. Well, these are still fairly new features (and in beta), so let’s hope the situation improves soon.

Hopefully, tag templates in general will be engineered to be consent-aware where necessary. It makes a whole lot of sense for templates that can work without storage to conditionally block setting cookies if there isn’t appropriate consent available.

Similarly, vendors that do have a separate endpoint for “privacy-friendly” data collection (e.g. no third-party cookies) should make the decision which endpoint to use dependent on the state of the relevant consent.

As before, configuring consent management is a lot of work. This is by design, as consent needs to be deliberately integrated and engineered, as there are legal consequences for ignoring the granularity of what user choice actually is.

With these new consent settings in GTM, thankfully it’s no longer the Google Tag Manager admin who has to do the bulk of the work, but vendors are required to participate and cooperate as well.


Whenever Google Tag Manager makes a conscious effort to make life easier for its admins and users, it makes me happy.

Integrating consent management platforms with Google Tag Manager has historically been a veritable pain in the rump. The new consent features don’t take away all of the friction, but they do make it more manageable to work together with CMPs to make sure that the GTM container is wired for consent options.

Similarly, service vendors now have new template APIs that allow them to add more granularity to their tags than just block vs. fire based on consent choice. I really, really hope vendors will make use of these features.

The new UI features make it easy to quickly audit a container for consent support, but I did find myself craving more even more transparency from the tag settings directly. It’s good to know what the built-in consent choices are, for example, but it would be even more useful to know how the tag actually relies on consent without having to dig into the template code itself.

Other things that I feel like should be improved include:

  • Incentivize using the new Consent Initialization and Initialization trigger types. Make them work with asynchronous requests as well (i.e. don’t proceed to the next trigger event until a success signal has been sent).
  • Make it possible to grant / deny consent on a vendor-by-vendor basis as well.
  • Make it possible to add custom purpose types instead of just the seven consent types that are now available (although that is a whole lot better than the two (ad_storage and analytics_storage) we used to have!).
  • Expose consent state to outside Google Tag Manager as well, as it’s possible that not all tags are running through Google Tag Manager.

In general, consent is predicated on transparency. The more GTM can improve transparency to what the state of consent is, the better for all parties involved.

What do you think about the current state of consent management in Google Tag Manager?