Skill Tracer
Skills › Marketing Skills › conversion-tracking › detect-acuity-scheduling.json

detect-acuity-scheduling.json

160 lines · skills/conversion-tracking/assets/gtm-recipes/detect/detect-acuity-scheduling.json at 88c79f6

1{
2 "exportFormatVersion": 2,
3 "exportTime": "2026-10-15 00:00:00",
4 "containerVersion": {
5 "path": "accounts/0/containers/0/versions/0",
6 "accountId": "0",
7 "containerId": "0",
8 "containerVersionId": "0",
9 "container": {
10 "path": "accounts/0/containers/0",
11 "accountId": "0",
12 "containerId": "0",
13 "name": "Conversion Detection - Acuity Scheduling",
14 "publicId": "GTM-XXXXXXX",
15 "usageContext": [
16 "WEB"
17 ],
18 "fingerprint": "0",
19 "tagManagerUrl": "https://tagmanager.google.com/#/container/accounts/0/containers/0/workspaces/0"
20 },
21 "tag": [
22 {
23 "accountId": "0",
24 "containerId": "0",
25 "tagId": "1",
26 "name": "Conversion Detection - Acuity Scheduling Booking Listener",
27 "type": "html",
28 "parameter": [
29 {
30 "type": "TEMPLATE",
31 "key": "html",
32 "value": "<script>\n(function () {\n 'use strict';\n if (window.__ctDetectAcuity) return;\n window.__ctDetectAcuity = true;\n window.dataLayer = window.dataLayer || [];\n\n // Acuity's booking form gives the page no booking event of its own. This\n // listener pairs with a short snippet the site owner pastes into Acuity's\n // custom conversion tracking field (the field snippet that comes with this\n // listener). Acuity runs that snippet after each booking inside its\n // conversion tracking frame, on\n // https://conversion-tracking-sandbox.acuityinnovation.com, two frames below\n // this page, and it posts the booking's ID to this page only (it names the\n // page's own address as the target). The frame is a grandchild, so it can't\n // be matched to an iframe on the page; it must come from Acuity's origin and\n // sit inside this page's own frame tree.\n var ORIGIN_PATTERN = /^https:\\/\\/([a-z0-9-]+\\.)+acuityinnovation\\.com$/;\n var ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;\n var KIND_PATTERN = /^[A-Za-z_-]{1,32}$/;\n var PRICE_PATTERN = /^[0-9]{1,9}(\\.[0-9]{1,4})?$/;\n var seenIds = [];\n\n function isPlainObject(value) {\n return !!value && typeof value === 'object' && Object.prototype.toString.call(value) === '[object Object]';\n }\n\n // A frame inside this page has this page as its top window. A popup or a\n // window this page opened does not.\n function insideThisPage(source) {\n try { return source.top === window; } catch (e) { return false; }\n }\n\n function idSeen(id) {\n for (var i = 0; i < seenIds.length; i++) if (seenIds[i] === id) return true;\n return false;\n }\n\n function cleanText(value, max) {\n if (typeof value !== 'string') return '';\n value = value.replace(/[\\u0000-\\u001f\\u007f]/g, ' ').replace(/^\\s+|\\s+$/g, '');\n return value.length > max ? value.slice(0, max) : value;\n }\n\n function onMessage(message) {\n if (!message || !message.source || message.source === window) return;\n if (typeof message.origin !== 'string' || !ORIGIN_PATTERN.test(message.origin)) return;\n if (!insideThisPage(message.source)) return;\n var data = message.data;\n if (!isPlainObject(data) || data.source !== 'gtm-acuity-booking' || data.version !== 1) return;\n if (typeof data.appointment_id !== 'string' || !ID_PATTERN.test(data.appointment_id)) return;\n if (idSeen(data.appointment_id)) return;\n var push = { event: 'acuity_appointment_scheduled', appointment_id: data.appointment_id };\n if (typeof data.kind === 'string' && KIND_PATTERN.test(data.kind)) push.acuity_type = data.kind;\n var type = cleanText(data.appointment_type, 200);\n if (type) push.appointment_type = type;\n if (typeof data.price === 'string' && PRICE_PATTERN.test(data.price)) push.price = data.price;\n // The ID is remembered only once the push went through, so a dataLayer\n // that throws (a broken wrapper) never escapes into the page, and a\n // repeat of the same message can still be counted.\n try {\n window.dataLayer = window.dataLayer || [];\n window.dataLayer.push(push);\n } catch (e) {\n return;\n }\n seenIds.push(data.appointment_id);\n }\n\n window.addEventListener('message', onMessage, false);\n})();\n</script>"
33 },
34 {
35 "type": "BOOLEAN",
36 "key": "supportDocumentWrite",
37 "value": "false"
38 }
39 ],
40 "firingTriggerId": [
41 "2147479553"
42 ],
43 "tagFiringOption": "ONCE_PER_EVENT",
44 "monitoringMetadata": {
45 "type": "MAP"
46 },
47 "consentSettings": {
48 "consentStatus": "NOT_SET"
49 }
50 }
51 ],
52 "trigger": [
53 {
54 "accountId": "0",
55 "containerId": "0",
56 "triggerId": "2",
57 "name": "CE - Acuity Appointment Scheduled",
58 "type": "CUSTOM_EVENT",
59 "customEventFilter": [
60 {
61 "type": "EQUALS",
62 "parameter": [
63 {
64 "type": "TEMPLATE",
65 "key": "arg0",
66 "value": "{{_event}}"
67 },
68 {
69 "type": "TEMPLATE",
70 "key": "arg1",
71 "value": "acuity_appointment_scheduled"
72 }
73 ]
74 }
75 ],
76 "filter": []
77 }
78 ],
79 "variable": [
80 {
81 "accountId": "0",
82 "containerId": "0",
83 "variableId": "1",
84 "name": "DLV - Acuity Appointment ID",
85 "type": "v",
86 "parameter": [
87 {
88 "type": "TEMPLATE",
89 "key": "name",
90 "value": "appointment_id"
91 },
92 {
93 "type": "INTEGER",
94 "key": "dataLayerVersion",
95 "value": "2"
96 }
97 ]
98 },
99 {
100 "accountId": "0",
101 "containerId": "0",
102 "variableId": "2",
103 "name": "DLV - Acuity Type",
104 "type": "v",
105 "parameter": [
106 {
107 "type": "TEMPLATE",
108 "key": "name",
109 "value": "acuity_type"
110 },
111 {
112 "type": "INTEGER",
113 "key": "dataLayerVersion",
114 "value": "2"
115 }
116 ]
117 },
118 {
119 "accountId": "0",
120 "containerId": "0",
121 "variableId": "3",
122 "name": "DLV - Acuity Appointment Type",
123 "type": "v",
124 "parameter": [
125 {
126 "type": "TEMPLATE",
127 "key": "name",
128 "value": "appointment_type"
129 },
130 {
131 "type": "INTEGER",
132 "key": "dataLayerVersion",
133 "value": "2"
134 }
135 ]
136 },
137 {
138 "accountId": "0",
139 "containerId": "0",
140 "variableId": "4",
141 "name": "DLV - Acuity Price",
142 "type": "v",
143 "parameter": [
144 {
145 "type": "TEMPLATE",
146 "key": "name",
147 "value": "price"
148 },
149 {
150 "type": "INTEGER",
151 "key": "dataLayerVersion",
152 "value": "2"
153 }
154 ]
155 }
156 ],
157 "fingerprint": "0",
158 "tagManagerUrl": "https://tagmanager.google.com/#/container/accounts/0/containers/0/versions/0"
159 }