Skills › Marketing Skills › conversion-tracking › detect-framer-forms.json
detect-framer-forms.json
103 lines · skills/conversion-tracking/assets/gtm-recipes/detect/detect-framer-forms.json at 88c79f6
1{
2 "exportFormatVersion": 2,
3 "exportTime": "2026-07-02 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 - Framer Forms",
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 - Framer Forms Submission Listener",
27 "type": "html",
28 "parameter": [
29 {
30 "type": "TEMPLATE",
31 "key": "html",
32 "value": "<script>\n(function () {\n if (window.__ctFramerForms) return;\n window.__ctFramerForms = true;\n window.dataLayer = window.dataLayer || [];\n var listeners = [];\n var pending = [];\n window.__ctFramerSubscribe = function (listener) {\n if (listener && typeof listener === 'object') listeners.push(listener);\n };\n function isFramerForm(form) {\n try {\n if (!form || String(form.tagName || '').toLowerCase() !== 'form') return false;\n if (form.hasAttribute && form.hasAttribute('data-framer-name')) return true;\n if (typeof form.className === 'string' && /(^|\\s)framer-[A-Za-z0-9_-]+/.test(form.className)) return true;\n return !!(form.closest && (form.closest('[data-framer-component-type]') || form.closest('[data-framer-name]')));\n } catch (e) { return false; }\n }\n function notify(phase, form, candidate) {\n var handled = false;\n for (var i = 0; i < listeners.length; i++) {\n try {\n var fn = listeners[i] && listeners[i][phase];\n if (typeof fn === 'function' && fn(form, candidate) === true) handled = true;\n } catch (e) {}\n }\n return handled;\n }\n function remember(form) {\n try {\n if (!isFramerForm(form)) return;\n for (var i = 0; i < pending.length; i++) {\n if (pending[i].form === form) return;\n }\n pending.push({ form: form, time: Date.now(), claimed: false });\n notify('beforeAttempt', form, false);\n } catch (e) {}\n }\n function finish(attempts, accepted) {\n for (var i = 0; i < attempts.length; i++) {\n var index = pending.indexOf(attempts[i]);\n if (index !== -1) pending.splice(index, 1);\n }\n if (!accepted) return;\n // Concurrent forms cannot be attributed without inspecting answers.\n // Keep those submissions event-only, with no guessed form name.\n var form = attempts.length === 1 ? attempts[0].form : null;\n var name = form ? form.getAttribute('data-framer-name') || form.id || '' : '';\n if (!form || !notify('onCandidate', form, true)) {\n window.dataLayer.push({ event: 'framer_form_submitted', form_name: name });\n }\n }\n if (typeof window.fetch === 'function') {\n var originalFetch = window.fetch;\n window.fetch = function (input, options) {\n var attempts = null;\n try {\n var url = typeof input === 'string' ? input : input && input.url;\n var method = options && options.method || input && input.method || 'GET';\n if (String(method).toUpperCase() === 'POST' && /^https:\\/\\/api\\.framer\\.com\\/forms\\/v1\\/forms\\/[a-f0-9-]+\\/submit(?:\\?.*)?$/.test(url || '')) {\n attempts = [];\n var now = Date.now();\n pending = pending.filter(function (attempt) { return now - attempt.time < 60000; });\n for (var i = 0; i < pending.length; i++) {\n if (!pending[i].claimed) {\n pending[i].claimed = true;\n attempts.push(pending[i]);\n }\n }\n }\n } catch (e) {}\n var result = originalFetch.apply(this, arguments);\n if (attempts) result.then(function (response) {\n try { finish(attempts, response.ok === true); } catch (e) {}\n }, function () {\n try { finish(attempts, false); } catch (e) {}\n });\n return result;\n };\n }\n if (typeof document !== 'undefined' && document.addEventListener) {\n document.addEventListener('submit', function (event) { remember(event.target); }, true);\n // Framer handles Enter directly instead of dispatching native submit.\n document.addEventListener('keydown', function (event) {\n var target = event.target;\n if (event.key === 'Enter' && target && String(target.tagName).toLowerCase() !== 'textarea') {\n var form = target.form;\n if (isFramerForm(form) && form.matches(':valid')) remember(form);\n }\n }, true);\n }\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 - Framer Form Submitted",
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": "framer_form_submitted"
72 }
73 ]
74 }
75 ],
76 "filter": []
77 }
78 ],
79 "variable": [
80 {
81 "accountId": "0",
82 "containerId": "0",
83 "variableId": "1",
84 "name": "DLV - Framer Form Name",
85 "type": "v",
86 "parameter": [
87 {
88 "type": "TEMPLATE",
89 "key": "name",
90 "value": "form_name"
91 },
92 {
93 "type": "INTEGER",
94 "key": "dataLayerVersion",
95 "value": "2"
96 }
97 ]
98 }
99 ],
100 "fingerprint": "0",
101 "tagManagerUrl": "https://tagmanager.google.com/#/container/accounts/0/containers/0/workspaces/0"
102 }
103}