# Google Consent Mode v2





OptinStack drives [Google Consent Mode v2](https://developers.google.com/tag-platform/security/guides/consent) automatically. On every consent state change, the runtime calls `gtag('consent', ...)` with the mapped signals and pushes OptinStack events to `dataLayer` for GTM triggers.

## How it works [#how-it-works]

When the consent kernel initializes:

1. OptinStack ensures `window.dataLayer` exists (creates an empty array if missing).
2. OptinStack ensures `window.gtag` exists (installs a minimal function that pushes to `dataLayer` if missing).
3. The first Consent Mode call uses `gtag('consent', 'default', …)` and includes `wait_for_update: 500`.
4. Later consent changes use `gtag('consent', 'update', …)`.
5. DataLayer events such as `optinstack_consent_updated` are pushed for use in GTM triggers.

You do **not** need to pre-install `gtag` for Consent Mode signals. You still need Google tags (or GTM) on the page for those tags to *consume* the signals.

Place the OptinStack script **before** GTM / gtag so default consent is set early. See [Install](/install).

<Callout type="info">
  Tracker **blocking** (scripts, iframes, cookies, storage) is separate from Consent Mode. Blocking uses your published
  inventory and `data-optinstack-categories`. Consent Mode only updates Google's consent signals. See [Basic vs Advanced
  Google Consent Mode](https://community.optinstack.com/articles/basic-vs-advanced-consent-mode) for when cookieless
  pings may still fire.
</Callout>

## Consent signal mapping [#consent-signal-mapping]

| GTM parameter             | OptinStack category |
| ------------------------- | ------------------- |
| `ad_personalization`      | `marketing`         |
| `ad_storage`              | `marketing`         |
| `ad_user_data`            | `marketing`         |
| `analytics_storage`       | `analytics`         |
| `functionality_storage`   | `preferences`       |
| `personalization_storage` | `preferences`       |
| `security_storage`        | `necessary`         |

Each parameter is set to `'granted'` or `'denied'` based on the corresponding OptinStack consent category, with the exceptions below.

## Disabled categories [#disabled-categories]

Project configuration can disable entire categories (for example, hide the marketing toggle in consent preferences). When a category is **disabled*&#x2A; in config, the SDK treats it as essential for enforcement purposes and sends &#x2A;*`granted`** for the mapped Google Consent Mode signals on that category. Disabled categories are not shown in the preferences UI and cannot be declined by visitors.

## Preview and override modes [#preview-and-override-modes]

Dashboard preview or rule-override flows can force a temporary banner rule in the browser. When an override forces an **opt-in** rule for preview, Consent Mode updates may be skipped so preview behavior does not fight production Google tags. Production traffic uses the published regional policy and always drives Consent Mode as described above.

## OptinStack GTM guides [#optinstack-gtm-guides]

For full setup walkthroughs, see:

* [Integrate OptinStack with Google Tag Manager](https://community.optinstack.com/articles/integrate-optinstack-with-google-tag-manager)
* [Set up Google Consent Mode v2](https://community.optinstack.com/articles/set-up-google-consent-mode-v2)
* [Basic vs Advanced Google Consent Mode](https://community.optinstack.com/articles/basic-vs-advanced-consent-mode)
* [OptinStack and Google Tag Manager integration](https://optinstack.com/integrations/google-tag-manager)

## Example [#example]

When a user accepts all categories, the SDK sends:

```js
gtag('consent', 'update', {
  ad_personalization: 'granted',
  ad_storage: 'granted',
  ad_user_data: 'granted',
  analytics_storage: 'granted',
  functionality_storage: 'granted',
  personalization_storage: 'granted',
  security_storage: 'granted',
});
```

When a user rejects all optional categories:

```js
gtag('consent', 'update', {
  ad_personalization: 'denied',
  ad_storage: 'denied',
  ad_user_data: 'denied',
  analytics_storage: 'denied',
  functionality_storage: 'denied',
  personalization_storage: 'denied',
  security_storage: 'granted', // necessary is always granted
});
```

## DataLayer events [#datalayer-events]

The SDK also pushes events to the `dataLayer` array for use in GTM triggers:

| DataLayer event                    | When                                                            |
| ---------------------------------- | --------------------------------------------------------------- |
| `optinstack_gpc_signal_detected`   | On init, if Global Privacy Control is active                    |
| `optinstack_consent_updated`       | Every time consent state changes                                |
| `optinstack_necessary_accepted`    | First time necessary is granted (fires once)                    |
| `optinstack_analytics_accepted`    | First time analytics is granted (fires once)                    |
| `optinstack_marketing_accepted`    | First time marketing is granted (fires once)                    |
| `optinstack_preferences_accepted`  | First time preferences is granted (fires once)                  |
| `optinstack_unclassified_accepted` | First time all four consent categories are granted (fires once) |

### Using dataLayer events in GTM [#using-datalayer-events-in-gtm]

Create a custom trigger in GTM based on these events:

1. Go to **Triggers** → **New**
2. Select **Custom Event**
3. Set the event name to `optinstack_analytics_accepted`
4. Use this trigger on tags that should only fire after the user grants analytics consent

<Callout type="info">
  The `*_accepted` events fire only once per page load. They will not re-fire if consent is toggled off and on again
  within the same load. If a category is later denied, the corresponding accepted marker is cleared so a new grant can
  fire again later in the session.
</Callout>

## Webflow Insights [#webflow-insights]

If your Webflow project uses Webflow Insights and follows an **opt-in** GDPR policy rule, configure Webflow Insights so that tracking is disabled until the user has granted consent.
This ensures analytics are not collected before the user has explicitly opted in.
<img alt="Webflow Insights" src="__img0" />
Inside Webflow designer, navigate to **Insights → Tracking** and set **Tracking defaults** to **Don't track by default**.

The runtime also syncs analytics consent with Webflow's user-tracking APIs (`wf.allowUserTracking` / `wf.denyUserTracking`) when those APIs are present.
