___TERMS_OF_SERVICE___ By creating or modifying this file you agree to Google Tag Manager's Community Template Gallery Developer Terms of Service available at https://developers.google.com/tag-manager/gallery-tos (or such other URL as Google may provide), as modified from time to time. ___INFO___ { "displayName": "OptinStack", "description": "Bridges OptinStack consent into Google Tag Manager. Reads the visitor's OptinStack consent and drives Google Consent Mode v2 (default + update) via the GTM consent API, including per-region default states. Optionally injects the OptinStack runtime script for teams that prefer to run everything through GTM.", "categories": [ "TAG_MANAGEMENT", "PERSONALIZATION" ], "securityGroups": [], "id": "optinstack", "type": "TAG", "version": 1, "brand": { "thumbnail": "data:image/png;base64,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", "displayName": "OptinStack", "id": "OptinStack" }, "containerContexts": [ "WEB" ] } ___VENDOR_DETAILS___ { "vendorName": "OptinStack LLC", "description": "OptinStack is a Consent Management Platform (CMP) by OptinStack LLC. It renders a consent banner and preferences dialog in an isolated Shadow DOM, blocks and releases trackers by category, syncs to Google Consent Mode v2, and honors Global Privacy Control (GPC).", "homepageUrl": "https://optinstack.com", "freeTier": "Free plan for each eligible assigned hostname by default", "price": "Free, Pro ($7/month), Business ($15/month), Enterprise (custom). Free can cover each eligible hostname by default; paid plans attach per hostname. See https://optinstack.com for current pricing.", "countries": "No limitations", "platforms": [ "WEB" ], "termsAndConditionsUrl": "https://optinstack.com/legal/terms", "defaultSettingsUrl": "https://docs.optinstack.com" } ___TEMPLATE_PARAMETERS___ [ { "type": "CHECKBOX", "name": "consentModeEnabled", "checkboxText": "Enable Google Consent Mode v2", "simpleValueType": true, "defaultValue": true, "help": "Read the visitor's OptinStack consent and signal it to Google tags via the Google Consent Mode v2 API (gtag consent). Recommended firing trigger: Consent Initialization - All Pages, so defaults are set before other tags load.", "alwaysInSummary": true }, { "type": "GROUP", "name": "ConsentModeSettings", "displayName": "Consent Mode Settings", "groupStyle": "NO_ZIPPY", "subParams": [ { "type": "TEXT", "name": "waitForUpdate", "displayName": "Wait for update", "simpleValueType": true, "valueUnit": "milliseconds", "help": "How long tags configured for Consent Mode should wait (in ms) for the OptinStack banner to receive a choice before proceeding with the default state.", "defaultValue": 500, "valueValidators": [ { "type": "NON_EMPTY" }, { "type": "NON_NEGATIVE_NUMBER" } ] }, { "type": "SELECT", "name": "adsDataRedaction", "displayName": "Redact ads data", "selectItems": [ { "value": true, "displayValue": "True" }, { "value": false, "displayValue": "False" }, { "value": "dynamic", "displayValue": "Dynamic (match ad_storage)" } ], "simpleValueType": true, "defaultValue": "dynamic", "help": "When ad data redaction is true and ad_storage is denied, ad click identifiers in outbound Google requests are redacted. Dynamic mirrors the marketing/ad_storage consent state." }, { "type": "CHECKBOX", "name": "urlPassthrough", "checkboxText": "Enable URL passthrough", "simpleValueType": true, "help": "When enabled, a few query parameters (e.g. gclid) may be appended to links so click information survives page navigation under Consent Mode." }, { "type": "GROUP", "name": "DefaultConsent", "displayName": "Default Consent State", "subParams": [ { "type": "PARAM_TABLE", "name": "regionSettings", "paramTableColumns": [ { "param": { "type": "TEXT", "name": "region", "displayName": "Region", "simpleValueType": true, "help": "Leave blank to apply globally. Otherwise use ISO-3166-1 alpha-2 country codes (e.g. DE, US, GB). Comma-separate multiple regions." }, "isUnique": true }, { "param": { "type": "SELECT", "name": "defaultConsentPreferences", "displayName": "Preferences", "selectItems": [ { "value": "denied", "displayValue": "Denied" }, { "value": "granted", "displayValue": "Granted" } ], "simpleValueType": true, "help": "Default state for the preferences category. Maps to Google Consent Mode: functionality_storage, personalization_storage.", "defaultValue": "denied" }, "isUnique": false }, { "param": { "type": "SELECT", "name": "defaultConsentAnalytics", "displayName": "Analytics", "selectItems": [ { "value": "denied", "displayValue": "Denied" }, { "value": "granted", "displayValue": "Granted" } ], "simpleValueType": true, "defaultValue": "denied", "help": "Default state for the analytics category. Maps to Google Consent Mode: analytics_storage." }, "isUnique": false }, { "param": { "type": "SELECT", "name": "defaultConsentMarketing", "displayName": "Marketing", "selectItems": [ { "value": "denied", "displayValue": "Denied" }, { "value": "granted", "displayValue": "Granted" } ], "simpleValueType": true, "defaultValue": "denied", "help": "Default state for the marketing category. Maps to Google Consent Mode: ad_storage, ad_user_data, ad_personalization." }, "isUnique": false } ], "editRowTitle": "Edit region", "newRowButtonText": "Add region", "newRowTitle": "Add region" } ], "help": "The default consent state used until the visitor makes a choice in the OptinStack banner. You can set different defaults per region. Use ISO-3166-1 alpha-2 country codes (e.g. DE, US, GB) for region values; leave blank for the global default." } ], "enablingConditions": [ { "paramName": "consentModeEnabled", "paramValue": true, "type": "EQUALS" } ] }, { "type": "CHECKBOX", "name": "injectRuntime", "checkboxText": "Load the OptinStack runtime through GTM (optional)", "simpleValueType": true, "defaultValue": false, "help": "Off by default. Most teams install the OptinStack script directly in their site (recommended). Enable this only if you prefer to manage the entire stack through GTM. When enabled, provide your Site ID below and GTM will inject the runtime." }, { "notSetText": "Please enter your OptinStack Site ID from your dashboard > Install tab.", "help": "Create an account at optinstack.com, open your project, and copy the Site ID from the Install tab. GTM will load the OptinStack runtime (optinstack.js) for this site.", "valueValidators": [ { "type": "NON_EMPTY" } ], "displayName": "Site ID", "simpleValueType": true, "name": "siteId", "type": "TEXT", "valueHint": "Your OptinStack site ID", "enablingConditions": [ { "paramName": "injectRuntime", "paramValue": true, "type": "EQUALS" } ] } ] ___SANDBOXED_JS_FOR_WEB_TEMPLATE___ const injectScript = require('injectScript'); const queryPermission = require('queryPermission'); const gtagSet = require('gtagSet'); const setDefaultConsentState = require('setDefaultConsentState'); const updateConsentState = require('updateConsentState'); const getCookieValues = require('getCookieValues'); // Sandboxed JS does not expose these as globals; require them instead. const JSON = require('JSON'); const decodeUriComponent = require('decodeUriComponent'); const queue = require('queue'); // read the live OptinStack global safely (setInWindow/getFromWindow sandbox gate). const copyFromWindow = require('copyFromWindow'); const consentModeEnabled = data.consentModeEnabled; const waitForUpdate = data.waitForUpdate; const urlPassthrough = data.urlPassthrough; const adsDataRedaction = data.adsDataRedaction || 'dynamic'; const regionSettings = data.regionSettings || []; // OptinStack stores consent in a first-party cookie named `optinstack`. const COOKIE_NAME = 'optinstack'; /** * Map OptinStack's four consent categories onto the * Google Consent Mode v2 consent signals. * - marketing -> ad_storage, ad_user_data, ad_personalization * - analytics -> analytics_storage * - preferences -> functionality_storage, personalization_storage * - necessary -> security_storage (always granted) * * @param choices - OptinStack consent choices { necessary, analytics, marketing, preferences } * @returns Consent signal map for the Google consent API */ const toConsentSignals = (choices) => { const granted = 'granted'; const denied = 'denied'; const marketing = choices.marketing === true ? granted : denied; const analytics = choices.analytics === true ? granted : denied; const preferences = choices.preferences === true ? granted : denied; return { ad_storage: marketing, ad_user_data: marketing, ad_personalization: marketing, analytics_storage: analytics, functionality_storage: preferences, personalization_storage: preferences, security_storage: granted // necessary is an invariant and always granted }; }; /** * Read the OptinStack consent cookie and parse the stored choices. * The cookie value is URI-encoded JSON with the shape: * {"id":"...","choices":{"necessary":true,"analytics":false,...},"timestamp":...} * * Sandboxed JS has no try/catch, so we validate the string shape via * indexOf and extract boolean values by substring match instead of * JSON.parse. JSON booleans are always unquoted, so '"key":true' is * a stable marker regardless of key ordering or extra fields. * * Returns null when the cookie is absent or unrecognized so the caller * falls back to the configured default consent state. * * @returns OptinStack choices object or null */ const readStoredChoices = () => { const raw = getCookieValues(COOKIE_NAME).join(''); if (!raw) return null; const decoded = decodeUriComponent(raw); // Guard: locate the "choices" object inside the stored JSON. const choicesMarker = '"choices":{'; const choicesIdx = decoded.indexOf(choicesMarker); if (choicesIdx === -1) return null; // Extract the inner substring of the choices object. const objStart = decoded.indexOf('{', choicesIdx + choicesMarker.length - 1); const objEnd = decoded.indexOf('}', objStart); if (objStart === -1 || objEnd === -1) return null; const choicesStr = decoded.substring(objStart + 1, objEnd); // Match unquoted JSON booleans. necessary is an invariant (always true). return { necessary: true, analytics: choicesStr.indexOf('"analytics":true') !== -1, marketing: choicesStr.indexOf('"marketing":true') !== -1, preferences: choicesStr.indexOf('"preferences":true') !== -1 }; }; /** * Push the Google Consent Mode state derived from OptinStack's choices * to the GTM consent API. * * @param choices - OptinStack choices object */ const applyConsentSignals = (choices) => { const signals = toConsentSignals(choices); updateConsentState(signals); // Ads data redaction tracks the marketing signal in dynamic mode. const marketingGranted = choices.marketing === true; const redact = adsDataRedaction === 'dynamic' ? !marketingGranted : adsDataRedaction === true; gtagSet({ ads_data_redaction: redact, url_passthrough: urlPassthrough === true }); }; /** * Subscribe to live OptinStack consent changes and reflect them in GTM. * * OptinStack exposes several equivalent surfaces; this bridges all of them * so the template keeps working across runtime versions: * - window.OptinStack.subscribe(fn) (preferred, top-level; passes choices) * - window.OptinStack.on('choices', fn) (alias; passes choices) * - window.OptinStack.events.on('consent-updated', fn) * (passes { id, choices, previousChoices, action, source, timestamp }) * Falls back to polling window.OptinStack.choices if none are present yet. * * Sandboxed JS has no setTimeout/setInterval/window, so we use the * `queue` and `copyFromWindow` APIs instead. */ let pollPrev = ''; const pollChoices = () => { const api = copyFromWindow('OptinStack'); if (!api) { // The runtime has not booted yet; retry shortly. This is common when // the runtime is injected by GTM (async) or loads late on the page. queue.push(pollChoices); return; } // Preferred: top-level subscribe(fn) -> receives bare choices. if (typeof api.subscribe === 'function') { api.subscribe((choices) => applyConsentSignals(choices)); return; } // Alias: on('choices', fn) -> receives bare choices. if (typeof api.on === 'function') { api.on('choices', (choices) => applyConsentSignals(choices)); return; } // Explicit event bus: events.on('consent-updated', fn). // The payload is { id, choices, ... } so unwrap .choices. if (api.events && typeof api.events.on === 'function') { api.events.on('consent-updated', (detail) => { if (detail && detail.choices) applyConsentSignals(detail.choices); }); return; } // Fallback: poll the choices getter if no subscription surface exists. if (api.choices) { const next = JSON.stringify(api.choices); if (next !== pollPrev) { pollPrev = next; applyConsentSignals(api.choices); } queue.push(pollChoices); // re-schedule the next poll tick } }; // --- Consent Mode v2 wiring ------------------------------------------------- if (consentModeEnabled !== false) { // Turn a region string ("DE, US") into an array (["DE", "US"]). const toRegionArray = (regionStr) => regionStr.split(',').map((r) => r.trim()).filter((r) => r.length !== 0); // Set default consent for each configured region. let hasGlobalDefault = false; regionSettings.forEach((regionObj) => { const signals = { ad_storage: regionObj.defaultConsentMarketing, ad_user_data: regionObj.defaultConsentMarketing, ad_personalization: regionObj.defaultConsentMarketing, analytics_storage: regionObj.defaultConsentAnalytics, functionality_storage: regionObj.defaultConsentPreferences, personalization_storage: regionObj.defaultConsentPreferences, security_storage: 'granted' }; const regions = toRegionArray(regionObj.region || ''); if (regions.length) { signals.region = regions; } else { hasGlobalDefault = true; } if (waitForUpdate > 0) { signals.wait_for_update = waitForUpdate; } setDefaultConsentState(signals); }); // Fallback to all-denied if no global default was configured. if (!hasGlobalDefault) { const fallback = { ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied', analytics_storage: 'denied', functionality_storage: 'denied', personalization_storage: 'denied', security_storage: 'granted' }; if (waitForUpdate > 0) { fallback.wait_for_update = waitForUpdate; } setDefaultConsentState(fallback); } // If the visitor already has an OptinStack consent record, push an update // immediately so tags do not wait for the banner to re-render. const stored = readStoredChoices(); if (stored) { applyConsentSignals(stored); } // Keep GTM consent in sync when the visitor changes categories at runtime. pollChoices(); } // --- Optional runtime injection -------------------------------------------- // Most teams add the OptinStack script directly in their site . This // optional path is for teams that prefer to run the whole stack through GTM. if (data.injectRuntime === true && data.siteId) { // Fixed runtime URL: the latest build of optinstack.js. const scriptUrl = 'https://api.optinstack.com/v1/js/runtime/stable/optinstack.js'; // The runtime IIFE reads data-optinstack-site to know which project config // to load. We pass it as an attribute on the injected element. const injectAttrs = ' data-optinstack-site="' + data.siteId + '"'; if (queryPermission('inject_script', scriptUrl)) { injectScript(scriptUrl, data.gtmOnSuccess, data.gtmOnFailure, injectAttrs); } else { data.gtmOnFailure(); } } else { data.gtmOnSuccess(); } ___WEB_PERMISSIONS___ [ { "instance": { "key": { "publicId": "access_consent", "versionId": "1" }, "param": [ { "key": "consentTypes", "value": { "type": 2, "listItem": [ { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "analytics_storage" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "ad_storage" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "ad_user_data" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "ad_personalization" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "functionality_storage" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "personalization_storage" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "security_storage" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "consentType" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" } ], "mapValue": [ { "type": 1, "string": "wait_for_update" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "get_cookies", "versionId": "1" }, "param": [ { "key": "cookieAccess", "value": { "type": 1, "string": "specific" } }, { "key": "cookieNames", "value": { "type": 2, "listItem": [ { "type": 1, "string": "optinstack" } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "write_data_layer", "versionId": "1" }, "param": [ { "key": "keyPatterns", "value": { "type": 2, "listItem": [ { "type": 1, "string": "ads_data_redaction" }, { "type": 1, "string": "url_passthrough" } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "access_globals", "versionId": "1" }, "param": [ { "key": "keys", "value": { "type": 2, "listItem": [ { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "OptinStack" }, { "type": 8, "boolean": true }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "OptinStack.subscribe" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "OptinStack.on" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "OptinStack.events.on" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "inject_script", "versionId": "1" }, "param": [ { "key": "urls", "value": { "type": 2, "listItem": [ { "type": 1, "string": "https://api.optinstack.com/" } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": false } ] ___TESTS___ scenarios: [] ___NOTES___ OptinStack CMP Tag v1.0 * Initial release. A Google Consent Mode v2 bridge for OptinStack consent. Reads the visitor's OptinStack consent from the `optinstack` cookie and window.OptinStack.choices, and drives Consent Mode (default + update) via the GTM consent API. * Optional: load the OptinStack runtime through GTM (off by default). Most teams install the runtime script directly in their site . Recommended firing trigger: Consent Initialization - All Pages. Category mapping matches the runtime's built-in plugin: marketing -> ad_storage, ad_user_data, ad_personalization analytics -> analytics_storage preferences -> functionality_storage, personalization_storage necessary -> security_storage (always granted)