{"id":91216,"date":"2025-02-16T07:17:06","date_gmt":"2025-02-16T07:17:06","guid":{"rendered":"https:\/\/peraltafinancing.com\/analytics\/create-slack-notification-system-for-google-tag-manager-changes\/"},"modified":"2025-02-16T07:17:06","modified_gmt":"2025-02-16T07:17:06","slug":"create-slack-notification-system-for-google-tag-manager-changes","status":"publish","type":"post","link":"https:\/\/fivemor.com\/?p=91216","title":{"rendered":"Create Slack Notification System For Google Tag Manager Changes"},"content":{"rendered":"<p> <br \/>\n<\/p>\n<div>\n<p>Until recently, I had a feature on <a href=\"https:\/\/www.gtmtools\/\">GTM Tools<\/a> that polled the user\u2019s <a href=\"https:\/\/tagmanager.google.com\/\">Google Tag Manager<\/a> container(s) for a recently published version. If one was found, a notification was sent to a <a href=\"https:\/\/www.slack.com\/apps\/\">Slack app<\/a>, which forwarded it to a workspace and channel of the user\u2019s choice.<\/p>\n<p>This was fine, except for the fact that polling the GTM and Slack APIs for dozens upon dozens of containers is a total resource hog, and the only way I can maintain GTM Tools is it doesn\u2019t have API leaks like that.<\/p>\n<p>I thus deprecated the feature. Feeling bad about the decision (and because I got a bunch of feedback that users were actually using the feature), I wanted to share a step-by-step guide on how anyone can setup a simple Slack notification system for changes in a Google Tag Manager container.<\/p>\n<div style=\"aspect-ratio: 2156 \/ 1164;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gtm-slack-notification-system.jpg\" title=\"Slack notification system for GTM changes\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1164\" width=\"2156\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gtm-slack-notification-system.jpg#ZgotmplZ\" alt=\"Slack notification system for GTM changes\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>It\u2019s a <a href=\"https:\/\/nodejs.org\/en\/\">Node.js<\/a> application running on <a href=\"https:\/\/cloud.google.com\/functions\/\">Google Cloud Functions<\/a>. Google Cloud Functions are serverless, self-contained functions on the <a href=\"https:\/\/cloud.google.com\/\">Google Cloud Platform<\/a> (GCP) that can be triggered with different stimuli, such as an HTTP request or a <a href=\"https:\/\/cloud.google.com\/pubsub\/docs\">Pub\/Sub<\/a> message. For our purposes, it\u2019s the perfect tech because what we\u2019re trying to do is a very simple operation that needs to run periodically with zero- or low-cost.<\/p>\n<p>                <span class=\"simmer\"><br \/>\n  <span class=\"close\">X<\/span><\/p>\n<p>\n    <span class=\"fa fa-md fa-bell\"\/><br \/>\n    <strong>The Simmer Newsletter<\/strong>\n  <\/p>\n<p>\n    Subscribe to the <a href=\"https:\/\/www.simoahava.com\/newsletter\/\">Simmer newsletter<\/a> to get the latest news and content from Simo Ahava into your email inbox!\n  <\/p>\n<p>  <\/span><\/p>\n<h2 id=\"how-it-works\">How it works<\/h2>\n<p>Once it\u2019s up and running, here\u2019s how the solution works:<\/p>\n<ol>\n<li>Every N minutes (you can choose the interval), a <em>cron job<\/em> running on <strong>Cloud Scheduler<\/strong> will send a Pub\/Sub message.<\/li>\n<li>This Pub\/Sub message triggers the <strong>Cloud Function<\/strong>, which hosts a simple, serverless <strong>Node.js<\/strong> application.<\/li>\n<li>The application downloads a <em>state object<\/em> from <strong>Cloud Storage<\/strong>, which contains the most recent information (since the previous triggering) about each Google Tag Manager container you\u2019ve specified in the application configuration*.<\/li>\n<li>Then, the application polls the <strong>Google Tag Manager API<\/strong> to fetch details about the <em>published<\/em> version of each container.<\/li>\n<li>If the application detects a change in the <em>version ID<\/em> of the published version (compared to that stored in the state object), it sends a message using a <strong>Slack Incoming Webhook<\/strong>.<\/li>\n<li>This webhook posts the message to the appropriate channel in the Slack workspace you\u2019ve chosen for the integration.<\/li>\n<\/ol>\n<div style=\"aspect-ratio: 3144 \/ 2782;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gtm-slack-process.jpg\" title=\"GTM Slack process\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"2782\" width=\"3144\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gtm-slack-process.jpg#ZgotmplZ\" alt=\"GTM Slack process\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Once everything is done, the application stores the new, updated state back into Google Cloud Storage and finishes execution.<\/p>\n<h2 id=\"what-youll-need\">What you\u2019ll need<\/h2>\n<p>To make the whole thing work, you\u2019ll need a <a href=\"https:\/\/console.cloud.google.com\/\">Google Cloud Platform account<\/a>. In addition to that, you\u2019ll need:<\/p>\n<ul>\n<li>Access to a <a href=\"https:\/\/www.slack.com\/\">Slack<\/a> workspace, with permissions to add a bot to a channel.<\/li>\n<li>A credit card (this solution will most likely be zero-cost, but you need to add your billing information regardless).<\/li>\n<li>A <a href=\"https:\/\/cloud.google.com\/billing\/docs\/how-to\/manage-billing-account\">Google Cloud billing account<\/a>.<\/li>\n<li>Permissions to add a new READ user to the Google Tag Manager container(s) you want to observe.<\/li>\n<\/ul>\n<h2 id=\"step-1-create-a-new-google-cloud-platform-project\">Step 1: Create a new Google Cloud Platform project<\/h2>\n<p>The first thing you\u2019ll need to do is create a new project in GCP.<\/p>\n<p>You can do this by following <a href=\"https:\/\/console.cloud.google.com\/projectcreate\">this link<\/a>.<\/p>\n<p>Give your project a name, and make sure you select a valid Billing Account for it.<\/p>\n<div style=\"aspect-ratio: 1130 \/ 864;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/create-new-gcp-project.jpg\" title=\"Create new GCP project\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"864\" width=\"1130\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/create-new-gcp-project.jpg#ZgotmplZ\" alt=\"Create new GCP project\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>The console will hang for a while as your new project is being created. Once the project is created, you can visit it via the project selector.<\/p>\n<h2 id=\"step-2-configure-the-gcp-project\">Step 2: Configure the GCP project<\/h2>\n<h3 id=\"enable-the-apis\">Enable the APIs<\/h3>\n<p>Next thing you\u2019ll need to do is enable the APIs that the application will require. You\u2019ll also need to create a <em>bucket<\/em> in Google Cloud Storage, but we\u2019ll get to that after the APIs have been enabled.<\/p>\n<p>While making sure your new project is selected in the console, type <code>Cloud Functions API<\/code> into the search bar and click the result.<\/p>\n<div style=\"aspect-ratio: 2706 \/ 220;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/cloud-functions-api.jpg\" title=\"Cloud Functions API\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"220\" width=\"2706\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/cloud-functions-api.jpg#ZgotmplZ\" alt=\"Cloud Functions API\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>In the screen that opens, click <strong>Enable<\/strong>.<\/p>\n<div style=\"aspect-ratio: 1484 \/ 610;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/enable-api.jpg\" title=\"Enable API\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"610\" width=\"1484\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/enable-api.jpg#ZgotmplZ\" alt=\"Enable API\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Do the same for <code>Tag Manager API<\/code>.<\/p>\n<h3 id=\"create-a-new-cloud-storage-bucket\">Create a new Cloud Storage bucket<\/h3>\n<p>Once you\u2019ve enabled these two APIs, it\u2019s time to create a new <strong>bucket<\/strong> in Google Cloud Storage. This is where we\u2019ll store the <em>state<\/em> of the GTM container. Follow <a href=\"https:\/\/console.cloud.google.com\/storage\/browser\">this link<\/a> to reach the storage browser.<\/p>\n<p>Click the big blue <strong>Create Bucket<\/strong> link in the middle of the screen.<\/p>\n<p>Give the bucket a global, unique name. I simply named it the same thing as my project: <code>simo-demo-gtm-slack<\/code>.<\/p>\n<p>As <strong>Location type<\/strong>, choose <strong>Region<\/strong>, and select one that\u2019s available for <a href=\"https:\/\/cloud.google.com\/functions\/docs\/locations\">Cloud Functions as well<\/a>. For example, I chose <code>europe-west1<\/code> as the region for the Google Cloud Storage bucket, and I\u2019ll use the same location eventually for the Cloud Function.<\/p>\n<div style=\"aspect-ratio: 1196 \/ 952;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gcs-location.jpg\" title=\"GCS location\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"952\" width=\"1196\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gcs-location.jpg#ZgotmplZ\" alt=\"GCS location\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Keep the <strong>storage class<\/strong> as the default (<strong>Standard<\/strong>).<\/p>\n<p>Keep <strong>access control<\/strong> as the default (<strong>Fine-grained<\/strong>).<\/p>\n<p>Don\u2019t touch the <strong>Advanced settings<\/strong>.<\/p>\n<p>Click the blue <strong>Create<\/strong> button when ready.<\/p>\n<div style=\"aspect-ratio: 1362 \/ 750;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gcs-bucket-ready.jpg\" title=\"Cloud Storage bucket ready\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"750\" width=\"1362\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gcs-bucket-ready.jpg#ZgotmplZ\" alt=\"Cloud Storage bucket ready\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Make sure you remember your <strong>bucket name<\/strong> (<code>simo-demo-gtm-slack<\/code> in my example) &#8211; you\u2019ll need it when configuring the Cloud Function.<\/p>\n<h2 id=\"step-3-create-a-new-slack-app\">Step 3: Create a new Slack app<\/h2>\n<h3 id=\"create-the-app\">Create the app<\/h3>\n<p>You need to create a new <strong>Slack app<\/strong>. This application will be in charge of forwarding the messages sent by the Cloud Function to your Slack workspace and channel.<\/p>\n<p>To create a new app, visit <a href=\"https:\/\/api.slack.com\/\">https:\/\/api.slack.com\/<\/a> and click the big green <strong>Start Building<\/strong> button in the middle of the screen.<\/p>\n<p>Give the app a <strong>name<\/strong> (this will appear as the name of the bot in Slack). Choose the <strong>workspace<\/strong> you want to add this bot to.<\/p>\n<div style=\"aspect-ratio: 1144 \/ 956;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/create-app.jpg\" title=\"Create Slack app\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"956\" width=\"1144\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/create-app.jpg#ZgotmplZ\" alt=\"Create Slack app\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>When done, click <strong>Create App<\/strong>.<\/p>\n<h3 id=\"create-an-incoming-webhook\">Create an incoming webhook<\/h3>\n<p>In the dashboard that opens, choose the <strong>Incoming Webhooks<\/strong> functionality.<\/p>\n<div style=\"aspect-ratio: 1944 \/ 1174;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/incoming-webhooks.jpg\" title=\"Incoming webhooks\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1174\" width=\"1944\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/incoming-webhooks.jpg#ZgotmplZ\" alt=\"Incoming webhooks\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>In the configuration screen that opens, click the toggle to activate webhooks.<\/p>\n<div style=\"aspect-ratio: 1470 \/ 696;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/activate-webhooks.jpg\" title=\"Activate webhooks\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"696\" width=\"1470\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/activate-webhooks.jpg#ZgotmplZ\" alt=\"Activate webhooks\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Then, scroll down the configuration screen and click the button to <strong>Add New Webhook To Workspace<\/strong>.<\/p>\n<div style=\"aspect-ratio: 1384 \/ 904;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/add-webhook-to-workspace.jpg\" title=\"Add webhook to workspace\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"904\" width=\"1384\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/add-webhook-to-workspace.jpg#ZgotmplZ\" alt=\"Add webhook to workspace\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Now you need to choose which channel to post the messages on, so select one from the list. Once done, click <strong>Allow<\/strong>.<\/p>\n<div style=\"aspect-ratio: 1110 \/ 788;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/allow-bot-channel.jpg\" title=\"Allow bot to Slack channel\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"788\" width=\"1110\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/allow-bot-channel.jpg#ZgotmplZ\" alt=\"Allow bot to Slack channel\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>You should see your new bot enter as a new user into the channel.<\/p>\n<div style=\"aspect-ratio: 1178 \/ 360;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/new-integration-slack.jpg\" title=\"New integration in Slack\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"360\" width=\"1178\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/new-integration-slack.jpg#ZgotmplZ\" alt=\"New integration in Slack\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>In the list of webhooks for your app, there should now be a new URL. Keep this tab open for a while, as you\u2019ll soon need the URL.<\/p>\n<div style=\"aspect-ratio: 1396 \/ 876;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/your-webhook-url.jpg\" title=\"Your webhook URL\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"876\" width=\"1396\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/your-webhook-url.jpg#ZgotmplZ\" alt=\"Your webhook URL\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<h2 id=\"step-4-configure-the-cloud-function\">Step 4: Configure the Cloud Function<\/h2>\n<p>Almost done!<\/p>\n<p>Now, download this archive:<\/p>\n<p><a href=\"https:\/\/github.com\/sahava\/gtm-slack-integration\/raw\/master\/function.zip\">https:\/\/github.com\/sahava\/gtm-slack-integration\/raw\/master\/function.zip<\/a><\/p>\n<p>Unzip the archive to a folder on your computer.<\/p>\n<h3 id=\"modify-the-configjson-file\">Modify the config.json file<\/h3>\n<p>Rename the <code>config.sample.json<\/code> to <code>config.json<\/code> and open the file for editing with a <strong>text editor<\/strong> (or whatever you use to edit JSON files normally).<\/p>\n<div style=\"aspect-ratio: 1008 \/ 352;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/unzipped-archive.jpg\" title=\"Unzipped archive\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"352\" width=\"1008\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/unzipped-archive.jpg#ZgotmplZ\" alt=\"Unzipped archive\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>This is what the default <code>config.json<\/code> looks like:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-json\" data-lang=\"json\">{\n  <span style=\"color:#1e90ff;font-weight:bold\">\"gcs\"<\/span>: {\n    <span style=\"color:#1e90ff;font-weight:bold\">\"bucketName\"<\/span>: <span style=\"color:#a50\">\"gcs-bucket-name\"<\/span>,\n    <span style=\"color:#1e90ff;font-weight:bold\">\"fileName\"<\/span>: <span style=\"color:#a50\">\"filename.json\"<\/span>\n  },\n  <span style=\"color:#1e90ff;font-weight:bold\">\"slackOutput\"<\/span>: [{\n    <span style=\"color:#1e90ff;font-weight:bold\">\"slackWebhookUrl\"<\/span>: <span style=\"color:#a50\">\"https:\/\/hooks.slack.com\/services\/YYYY\/XXXX\/ZZZZ\"<\/span>,\n    <span style=\"color:#1e90ff;font-weight:bold\">\"gtmContainers\"<\/span>: [<span style=\"color:#a50\">\"12345_23456\"<\/span>, <span style=\"color:#a50\">\"12345_34567\"<\/span>]\n  }],\n  <span style=\"color:#1e90ff;font-weight:bold\">\"verboseLogging\"<\/span>: <span style=\"color:#00a\">true<\/span>\n}<\/code><\/pre>\n<\/div>\n<p>Change the <code>bucketName<\/code> value to what you set when <a href=\"#create-a-new-cloud-storage-bucket\">creating the bucket<\/a>.<\/p>\n<p>Set the <code>fileName<\/code> to what you want to store the GTM state as. For all intents and purposes, this makes very little difference. You can just keep the name as <code>filename.json<\/code> if you wish.<\/p>\n<p>Set <code>slackWebhookUrl<\/code> to the URL of the incoming webhook you created in the <a href=\"#create-an-incoming-webhook\">previous chapter<\/a>.<\/p>\n<p>Now, populate the <code>gtmContainers<\/code> array with all the combinations of Google Tag Manager account ID and container ID that you want to analyze for changes. The syntax is <code>accountID_containerID<\/code>, so in the default <code>config.json<\/code>, there are two containers:<\/p>\n<ol>\n<li>Account ID <code>12345<\/code>, Container ID <code>23456<\/code><\/li>\n<li>Account ID <code>12345<\/code>, Container ID <code>34567<\/code><\/li>\n<\/ol>\n<p>You can find the account ID and container ID in the URL when visiting the container. The account ID is the first numerical string you see, and the container ID is the second one. For example, this is one of the containers I want to monitor:<\/p>\n<div style=\"aspect-ratio: 1454 \/ 168;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gtm-accountid-containerid.jpg\" title=\"GTM account ID container ID\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"168\" width=\"1454\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gtm-accountid-containerid.jpg#ZgotmplZ\" alt=\"GTM account ID container ID\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>And this is another one:<\/p>\n<div style=\"aspect-ratio: 1392 \/ 188;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gtm-accountid-containerid-2.jpg\" title=\"Another account ID container ID\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"188\" width=\"1392\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gtm-accountid-containerid-2.jpg#ZgotmplZ\" alt=\"Another account ID container ID\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>So the array would look like this:<\/p>\n<p><code>\"gtmContainers\": [\"23019854_8060344\", \"4702258931_12269166\"]<\/code><\/p>\n<blockquote>\n<p>Remember that you need enough access rights on these containers to add a new user to them!<\/p>\n<\/blockquote>\n<p>You can keep the <code>verboseLogging<\/code> setting as <code>true<\/code>. My final <code>config.json<\/code> now looks like this:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-json\" data-lang=\"json\">{\n  <span style=\"color:#1e90ff;font-weight:bold\">\"gcs\"<\/span>: {\n    <span style=\"color:#1e90ff;font-weight:bold\">\"bucketName\"<\/span>: <span style=\"color:#a50\">\"simo-demo-gtm-slack\"<\/span>,\n    <span style=\"color:#1e90ff;font-weight:bold\">\"fileName\"<\/span>: <span style=\"color:#a50\">\"filename.json\"<\/span>\n  },\n  <span style=\"color:#1e90ff;font-weight:bold\">\"slackOutput\"<\/span>: [{\n    <span style=\"color:#1e90ff;font-weight:bold\">\"slackWebhookUrl\"<\/span>: <span style=\"color:#a50\">\"https:\/\/hooks.slack.com\/services\/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\"<\/span>,\n    <span style=\"color:#1e90ff;font-weight:bold\">\"gtmContainers\"<\/span>: [<span style=\"color:#a50\">\"23019854_8060344\"<\/span>, <span style=\"color:#a50\">\"4702258931_12269166\"<\/span>]\n  }],\n  <span style=\"color:#1e90ff;font-weight:bold\">\"verboseLogging\"<\/span>: <span style=\"color:#00a\">true<\/span>\n}<\/code><\/pre>\n<\/div>\n<p><strong>Save<\/strong> the file when done.<\/p>\n<p>Then, create a new <strong>ZIP<\/strong> archive that has the following files from the folder:<\/p>\n<ul>\n<li><code>index.js<\/code><\/li>\n<li><code>config.json<\/code><\/li>\n<li><code>package.json<\/code><\/li>\n<\/ul>\n<blockquote>\n<p><strong>NOTE!<\/strong> The files must be in the <strong>root<\/strong> of the ZIP archive, so do <em>not<\/em> compress a folder with the files. There should be no folders in the ZIP.<\/p>\n<\/blockquote>\n<p>Keep this archive handy, you\u2019ll need it very shortly.<\/p>\n<h3 id=\"create-a-new-cloud-function\">Create a new Cloud Function<\/h3>\n<p>Go to the <a href=\"https:\/\/console.cloud.google.com\/functions\/list\">Cloud Functions dashboard<\/a>, and click the <strong>Create Function<\/strong> link.<\/p>\n<p>Give the function a name. Again, my lack of imagination steers me to name it the same as my project and my GCS bucket: <code>simo-demo-gtm-slack<\/code>.<\/p>\n<p>Keep the <strong>Memory allocated<\/strong> as its default (256MB), and then choose <strong>Cloud Pub\/Sub<\/strong> from the list of triggers. In the <strong>Topic<\/strong> drop-down, click <strong>Create new topic<\/strong>.<\/p>\n<div style=\"aspect-ratio: 1036 \/ 500;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/create-new-topic.jpg\" title=\"Create new Pub\/Sub topic\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"500\" width=\"1036\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/create-new-topic.jpg#ZgotmplZ\" alt=\"Create new Pub\/Sub topic\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Set the topic name to <code>gtm-slack-start<\/code> and click <strong>CREATE<\/strong>.<\/p>\n<div style=\"aspect-ratio: 1048 \/ 420;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/new-pubsub-topic.jpg\" title=\"New Pub\/Sub topic\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"420\" width=\"1048\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/new-pubsub-topic.jpg#ZgotmplZ\" alt=\"New Pub\/Sub topic\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>In the <strong>Source Code<\/strong> selection, choose <strong>ZIP upload<\/strong>. Set the <strong>Runtime<\/strong> to <strong>Node.js 10<\/strong>..<\/p>\n<p>For <strong>ZIP file<\/strong>, click <strong>Browse<\/strong> and locate the archive you just created with your modified <code>config.json<\/code> included in it.<\/p>\n<div style=\"aspect-ratio: 1074 \/ 566;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/new-zip-file.jpg\" title=\"New ZIP File\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"566\" width=\"1074\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/new-zip-file.jpg#ZgotmplZ\" alt=\"New ZIP File\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Next, click <strong>Browse<\/strong> next to <strong>Stage bucket<\/strong>, and choose the bucket you created in the beginning of this tutorial.<\/p>\n<div style=\"aspect-ratio: 796 \/ 330;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gcs-bucket-stage.jpg\" title=\"GCS stage bucket\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"330\" width=\"796\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gcs-bucket-stage.jpg#ZgotmplZ\" alt=\"GCS stage bucket\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>The Google Cloud Platform will use this bucket as a temporary location to <em>stage<\/em> the Cloud Functions files.<\/p>\n<p>For <strong>Function to execute<\/strong>, type: <code>getGtmInfo<\/code>.<\/p>\n<p>Finally, expand the <strong>Environment variables, networking, timeouts and more<\/strong> option, and set your Cloud Functions to run on the same region where you created the Cloud Storage bucket in.<\/p>\n<blockquote>\n<p>This isn\u2019t a necessary step &#8211; you can set the Cloud Functions to run on any region you wish. It does make the application run a bit smoother, and can keep any possible costs down.<\/p>\n<\/blockquote>\n<div style=\"aspect-ratio: 950 \/ 574;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/advanced-settings.jpg\" title=\"Advanced Settings\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"574\" width=\"950\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/advanced-settings.jpg#ZgotmplZ\" alt=\"Advanced Settings\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Finally, click the blue <strong>Create<\/strong> button at the bottom of the configuration screen to create your Cloud Function.<\/p>\n<p>If all goes well, the icon next to the function name should turn into a green checkmark.<\/p>\n<div style=\"aspect-ratio: 2364 \/ 388;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/cloud-function-ready.jpg\" title=\"Cloud function ready\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"388\" width=\"2364\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/cloud-function-ready.jpg#ZgotmplZ\" alt=\"Cloud function ready\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<h2 id=\"step-5-add-a-service-account-as-a-user-to-your-gtm-containers\">Step 5: Add a service account as a user to your GTM containers<\/h2>\n<p>When you created the Cloud Function, GCP automatically created a <strong>service account<\/strong> for you.<\/p>\n<p>Service accounts can be used for server-to-server access between different Google properties. In this case, we want the Cloud Functions to have access to the Google Tag Manager containers you specified in the <code>config.json<\/code>, so we need to add a <strong>service account<\/strong> from your project as a user in Google Tag Manager.<\/p>\n<p>To find the service account email address, browse to <a href=\"https:\/\/console.cloud.google.com\/iam-admin\/serviceaccounts\">https:\/\/console.cloud.google.com\/iam-admin\/serviceaccounts<\/a>. You should see an <strong>App Engine default service account<\/strong> here, with email:<\/p>\n<p><code><a href=\"https:\/\/www.simoahava.com\/cdn-cgi\/l\/email-protection\" class=\"__cf_email__\" data-cfemail=\"2d7462787f727d7f6267686e797264696d4c5d5d5e5d4259034a5e485f5b444e484c4e4e42584359034e4240\">[email\u00a0protected]<\/a><\/code><\/p>\n<p>If you don\u2019t see a service account here, you might need to wait 10 minutes or so after creating the function, and you might need to refresh the page.<\/p>\n<div style=\"aspect-ratio: 1518 \/ 418;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/service-account.jpg\" title=\"Service account\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"418\" width=\"1518\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/service-account.jpg#ZgotmplZ\" alt=\"Service account\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Now, copy the email address and add it as a new <strong>READ<\/strong> user to <em>all the containers you listed in <code>config.json<\/code><\/em>.<\/p>\n<div style=\"aspect-ratio: 2566 \/ 316;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gtm-user.jpg\" title=\"GTM user\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"316\" width=\"2566\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/gtm-user.jpg#ZgotmplZ\" alt=\"GTM user\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Once you\u2019ve done these steps, you Cloud Function has permission to communicate with these Google Tag Manager containers.<\/p>\n<h2 id=\"step-6-test-it\">Step 6: Test it<\/h2>\n<p>You can now <strong>test<\/strong> the setup.<\/p>\n<p>First, visit <a href=\"https:\/\/console.cloud.google.com\/functions\/list\">https:\/\/console.cloud.google.com\/functions\/list<\/a> and click your function name to open it.<\/p>\n<p>In the dashboard that opens, choose the <strong>TESTING<\/strong> tab. Here, click <strong>TEST THE FUNCTION<\/strong>.<\/p>\n<div style=\"aspect-ratio: 1384 \/ 1250;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/test-the-function.jpg\" title=\"Test the cloud function\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1250\" width=\"1384\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/test-the-function.jpg#ZgotmplZ\" alt=\"Test the cloud function\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>If all goes well, you should see a bunch of log entries like this:<\/p>\n<div style=\"aspect-ratio: 2520 \/ 702;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/test-function-worked.jpg\" title=\"Test function worked\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"702\" width=\"2520\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/test-function-worked.jpg#ZgotmplZ\" alt=\"Test function worked\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Importantly, the <strong>state file<\/strong> has now been created. Thus, any time you run the function, if the version of the published containers is different from the one stored in the state file, Slack will be notified.<\/p>\n<p>Let\u2019s try this!<\/p>\n<p>Go ahead and <strong>publish<\/strong> one of your containers. Remember to name the version to describe what was changed.<\/p>\n<div style=\"aspect-ratio: 2632 \/ 660;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/just-published-version.jpg\" title=\"Just published version\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"660\" width=\"2632\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/just-published-version.jpg#ZgotmplZ\" alt=\"Just published version\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Now, <strong>test the Cloud Function again<\/strong>. You should see a new Slack message from your integration.<\/p>\n<div style=\"aspect-ratio: 1024 \/ 446;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/slack-works.jpg\" title=\"Slack works\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"446\" width=\"1024\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/slack-works.jpg#ZgotmplZ\" alt=\"Slack works\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>If you see this message, then everything works as it should.<\/p>\n<h2 id=\"step-7-setup-cloud-scheduler-to-run-the-function-every-x-minutes\">Step 7: Setup Cloud Scheduler to run the function every X minutes<\/h2>\n<p>Final step is to automate the polling. You can use <strong>Cloud Scheduler<\/strong> for that. It\u2019s basically a managed <a href=\"https:\/\/en.wikipedia.org\/wiki\/Cron\">cron<\/a> service, which you can configure to trigger the Cloud Function every five minutes, for example.<\/p>\n<p>First, visit <a href=\"https:\/\/console.cloud.google.com\/cloudscheduler\">https:\/\/console.cloud.google.com\/cloudscheduler<\/a> and click <strong>CREATE JOB<\/strong> to create a new job.<\/p>\n<p>From the location selector, choose a location as close to your Cloud Function as possible. I chose <code>europe-west<\/code>, as that\u2019s the main region where my Cloud Function and Cloud Storage buckets are running.<\/p>\n<div style=\"aspect-ratio: 1118 \/ 858;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/scheduler-location.jpg\" title=\"Cloud Scheduler location\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"858\" width=\"1118\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/scheduler-location.jpg#ZgotmplZ\" alt=\"Cloud Scheduler location\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>In the next screen, set the following options.<\/p>\n<ul>\n<li><strong>Name<\/strong>: Whatever you wish, I chose <code>simo-demo-gtm-slack<\/code>.<\/li>\n<li><strong>Description<\/strong>: Leave empty.<\/li>\n<li><strong>Frequency<\/strong>: Use <a href=\"https:\/\/crontab.guru\/\">crontab.guru<\/a> if you don\u2019t know how to set schedule expressions. I chose <code>*\/5 * * * *<\/code> to fire the function <strong>every five minutes<\/strong>.<\/li>\n<li><strong>Timezone<\/strong>: Irrelevant &#8211; choose your own, for example.<\/li>\n<li><strong>Target<\/strong>: Choose <strong>Pub\/Sub<\/strong>.<\/li>\n<li><strong>Topic<\/strong>: <code>gtm-slack-start<\/code>.<\/li>\n<li><strong>Payload<\/strong>: <code>true<\/code>.<\/li>\n<\/ul>\n<div style=\"aspect-ratio: 1056 \/ 1026;\" class=\"figure nocaption\">\n<p>    <a href=\"https:\/\/www.simoahava.com\/images\/2020\/02\/scheduler-cron-job.jpg\" title=\"Scheduler cron job\"><\/p>\n<p>    <img decoding=\"async\" class=\"fig-img\" height=\"1026\" width=\"1056\" loading=\"lazy\" src=\"https:\/\/www.simoahava.com\/images\/2020\/02\/scheduler-cron-job.jpg#ZgotmplZ\" alt=\"Scheduler cron job\"\/><\/p>\n<p>    <\/a><\/p>\n<\/div>\n<p>Click <strong>CREATE<\/strong> when ready.<\/p>\n<p>Now, publish a different container version again, and wait for the scheduler to go off. As I chose <strong>every 5 minutes<\/strong>, it means I have to wait a maximum of five minutes to verify the setup works.<\/p>\n<h2 id=\"summary\">Summary<\/h2>\n<p>You\u2019ve now got a <strong>Cloud Scheduler<\/strong> running your <strong>Cloud Function<\/strong> periodically. Each time the Cloud Function runs, it pulls the most recent state object from <strong>Cloud Storage<\/strong>, and then it polls the <strong>Google Tag Manager API<\/strong> to check if any of the containers in the state object have a newly published version.<\/p>\n<p>If a published version is found, the Cloud Function notifies your <strong>Slack Workspace<\/strong> with a message that a new version has been found.<\/p>\n<p>You can follow the costs in your Google Cloud Platform <a href=\"https:\/\/console.cloud.google.com\/home\/dashboard\">dashboard<\/a>. With two containers, my setup costs <strong>nothing<\/strong>.<\/p>\n<blockquote>\n<p>Note that if you add many more containers to the setup, not only might your costs increase but you also might need to increase the timeout from 60 seconds to something higher from the Google Cloud Functions settings. Be sure to monitor the cloud <strong>logs<\/strong>!<\/p>\n<\/blockquote>\n<p>You can modify <code>index.js<\/code> to check other properties as well. For example, with some programming you could notify Slack about <em>what<\/em> changes were done. Another useful feature would be to notify if new users have been added to the container, though before you do this you must make sure it\u2019s OK to do this legally, as you\u2019d have to store the list of users in the state object. You <em>could<\/em> hash this list to mitigate this, though.<\/p>\n<p>Anyway, let me know in the comments if you have questions about the setup, or if you have other ideas for this type of a <em>diff<\/em>-machine!<\/p>\n<\/p><\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>Until recently, I had a feature on GTM Tools that polled the user\u2019s Google Tag Manager container(s) for a recently published version. If one was found, a notification was sent to a Slack app, which forwarded it to a workspace and channel of the user\u2019s choice. This was fine, except for the fact that polling [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":91217,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12033],"tags":[7957,1531,14495,11720,11155,2345,1791],"dealstore":[],"offerexpiration":[],"class_list":["post-91216","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-analytics","tag-create","tag-google","tag-manager","tag-notification","tag-slack","tag-system","tag-tag"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Create Slack Notification System For Google Tag Manager Changes - Som2ny Network<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/fivemor.com\/?p=91216\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Create Slack Notification System For Google Tag Manager Changes - Som2ny Network\" \/>\n<meta property=\"og:description\" content=\"Until recently, I had a feature on GTM Tools that polled the user\u2019s Google Tag Manager container(s) for a recently published version. If one was found, a notification was sent to a Slack app, which forwarded it to a workspace and channel of the user\u2019s choice. This was fine, except for the fact that polling [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fivemor.com\/?p=91216\" \/>\n<meta property=\"og:site_name\" content=\"Som2ny Network\" \/>\n<meta property=\"article:published_time\" content=\"2025-02-16T07:17:06+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/gtm-slack-notification-system.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2156\" \/>\n\t<meta property=\"og:image:height\" content=\"1164\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fivemor.com\/?p=91216#article\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/?p=91216\"},\"author\":{\"name\":\"admin\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371\"},\"headline\":\"Create Slack Notification System For Google Tag Manager Changes\",\"datePublished\":\"2025-02-16T07:17:06+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=91216\"},\"wordCount\":2319,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fivemor.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=91216#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/gtm-slack-notification-system.jpg\",\"keywords\":[\"create\",\"Google\",\"Manager\",\"notification\",\"Slack\",\"SYSTEM\",\"TAG\"],\"articleSection\":[\"Analytics\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fivemor.com\/?p=91216#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fivemor.com\/?p=91216\",\"url\":\"https:\/\/fivemor.com\/?p=91216\",\"name\":\"Create Slack Notification System For Google Tag Manager Changes - Som2ny Network\",\"isPartOf\":{\"@id\":\"https:\/\/fivemor.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/fivemor.com\/?p=91216#primaryimage\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/?p=91216#primaryimage\"},\"thumbnailUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/gtm-slack-notification-system.jpg\",\"datePublished\":\"2025-02-16T07:17:06+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/fivemor.com\/?p=91216#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fivemor.com\/?p=91216\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/?p=91216#primaryimage\",\"url\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/gtm-slack-notification-system.jpg\",\"contentUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/gtm-slack-notification-system.jpg\",\"width\":2156,\"height\":1164},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fivemor.com\/?p=91216#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/fivemor.com\/?bp_activities=1\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Create Slack Notification System For Google Tag Manager Changes\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/fivemor.com\/#website\",\"url\":\"https:\/\/fivemor.com\/\",\"name\":\"Som2ny Network\",\"description\":\"Daily Deals\",\"publisher\":{\"@id\":\"https:\/\/fivemor.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/fivemor.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/fivemor.com\/#organization\",\"name\":\"Som2ny Network\",\"url\":\"https:\/\/fivemor.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png\",\"contentUrl\":\"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png\",\"width\":300,\"height\":86,\"caption\":\"Som2ny Network\"},\"image\":{\"@id\":\"https:\/\/fivemor.com\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/fivemor.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png\",\"caption\":\"admin\"},\"sameAs\":[\"https:\/\/fivemor.com\"],\"url\":\"https:\/\/fivemor.com\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Create Slack Notification System For Google Tag Manager Changes - Som2ny Network","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/fivemor.com\/?p=91216","og_locale":"en_US","og_type":"article","og_title":"Create Slack Notification System For Google Tag Manager Changes - Som2ny Network","og_description":"Until recently, I had a feature on GTM Tools that polled the user\u2019s Google Tag Manager container(s) for a recently published version. If one was found, a notification was sent to a Slack app, which forwarded it to a workspace and channel of the user\u2019s choice. This was fine, except for the fact that polling [&hellip;]","og_url":"https:\/\/fivemor.com\/?p=91216","og_site_name":"Som2ny Network","article_published_time":"2025-02-16T07:17:06+00:00","og_image":[{"width":2156,"height":1164,"url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/gtm-slack-notification-system.jpg","type":"image\/jpeg"}],"author":"admin","twitter_card":"summary_large_image","twitter_misc":{"Written by":"admin","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fivemor.com\/?p=91216#article","isPartOf":{"@id":"https:\/\/fivemor.com\/?p=91216"},"author":{"name":"admin","@id":"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371"},"headline":"Create Slack Notification System For Google Tag Manager Changes","datePublished":"2025-02-16T07:17:06+00:00","mainEntityOfPage":{"@id":"https:\/\/fivemor.com\/?p=91216"},"wordCount":2319,"commentCount":0,"publisher":{"@id":"https:\/\/fivemor.com\/#organization"},"image":{"@id":"https:\/\/fivemor.com\/?p=91216#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/gtm-slack-notification-system.jpg","keywords":["create","Google","Manager","notification","Slack","SYSTEM","TAG"],"articleSection":["Analytics"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fivemor.com\/?p=91216#respond"]}]},{"@type":"WebPage","@id":"https:\/\/fivemor.com\/?p=91216","url":"https:\/\/fivemor.com\/?p=91216","name":"Create Slack Notification System For Google Tag Manager Changes - Som2ny Network","isPartOf":{"@id":"https:\/\/fivemor.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/fivemor.com\/?p=91216#primaryimage"},"image":{"@id":"https:\/\/fivemor.com\/?p=91216#primaryimage"},"thumbnailUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/gtm-slack-notification-system.jpg","datePublished":"2025-02-16T07:17:06+00:00","breadcrumb":{"@id":"https:\/\/fivemor.com\/?p=91216#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fivemor.com\/?p=91216"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/?p=91216#primaryimage","url":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/gtm-slack-notification-system.jpg","contentUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2025\/02\/gtm-slack-notification-system.jpg","width":2156,"height":1164},{"@type":"BreadcrumbList","@id":"https:\/\/fivemor.com\/?p=91216#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/fivemor.com\/?bp_activities=1"},{"@type":"ListItem","position":2,"name":"Create Slack Notification System For Google Tag Manager Changes"}]},{"@type":"WebSite","@id":"https:\/\/fivemor.com\/#website","url":"https:\/\/fivemor.com\/","name":"Som2ny Network","description":"Daily Deals","publisher":{"@id":"https:\/\/fivemor.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/fivemor.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/fivemor.com\/#organization","name":"Som2ny Network","url":"https:\/\/fivemor.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/#\/schema\/logo\/image\/","url":"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png","contentUrl":"https:\/\/fivemor.com\/wp-content\/uploads\/2026\/07\/4a0953c4-logo-300x86-1.png","width":300,"height":86,"caption":"Som2ny Network"},"image":{"@id":"https:\/\/fivemor.com\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/fivemor.com\/#\/schema\/person\/b85e3c3dc0e1daea076524dc8810c371","name":"admin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/fivemor.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/729ae85bf62b9917e93538db2f2688ca?s=96&r=g&default=https%3A%2F%2Ffivemor.com%2Fwp-content%2Fplugins%2Fbuddypress-first-letter-avatar%2Fimages%2Fdefault%2F96%2Flatin_a.png","caption":"admin"},"sameAs":["https:\/\/fivemor.com"],"url":"https:\/\/fivemor.com\/?author=1"}]}},"_links":{"self":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts\/91216","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=91216"}],"version-history":[{"count":0,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/posts\/91216\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=\/wp\/v2\/media\/91217"}],"wp:attachment":[{"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=91216"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=91216"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=91216"},{"taxonomy":"dealstore","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fdealstore&post=91216"},{"taxonomy":"offerexpiration","embeddable":true,"href":"https:\/\/fivemor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fofferexpiration&post=91216"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}