Form submission is one of the most critical conversion points on websites. Lead capture, newsletter signup, contact, order, survey: each one is a conversion. But the diversity of form platforms (Typeform, Tally, HubSpot, Shopify Forms, iframe embedded forms) makes conversion tracking complex.
This guide brings together the building blocks of form conversion tracking: event mechanisms, GTM integration, data enrichment, webhook-based data transfer, and CRM integration. You’ll also find links to detailed platform-specific implementation guides.
Form Conversion Tracking Fundamentals
Form conversion tracking is the process of accurately reporting a form submission to analytics tools (GA4, Google Ads, etc.) and CRM systems. There are 3 core components:
- Event capture: An event must fire when the form is submitted
- Data enrichment: Source, campaign, and user information must travel with the event
- Transfer: Events and data must be delivered to analytics/CRM systems
Event Mechanisms
Form platforms communicate events to the outside world through different methods. Choosing the right method is critical for reliable tracking.
dataLayer.push()
The standard method for working with Google Tag Manager. The form platform calls dataLayer.push() directly, and GTM captures the event:
window.dataLayer = window.dataLayer || [];
dataLayer.push({
event: "form_submitted",
formId: "contact-form",
formName: "Contact Form",
});
Platforms using this: Typeform (TypeformSubmit), Tally (Tally.FormSubmitted, with GA4 integration)
Advantage: GTM captures directly, no additional code needed.
window.postMessage
The most reliable method for iframe or embedded forms. The form platform sends a message from inside the iframe to the parent page:
// From inside the iframe (form platform)
window.parent.postMessage(
{
event: "formSubmitted",
formId: "abc123",
},
"*",
);
// On the parent page (listener)
window.addEventListener("message", function (event) {
if (event.origin === "https://form-platform.com") {
dataLayer.push({ event: "iframe_form_submitted" });
}
});
Platforms using this: Tally (free, all embeds), HubSpot Conversations (chat widget)
Advantage: Unaffected by third-party cookie restrictions, bypasses cross-domain issues.
For detailed implementation, see the Tracking iFrame Form Events with GTM guide.
Shadow DOM Event Listener
Some platforms render forms inside Shadow DOM. Standard DOM selectors won’t work; you need to access via shadowRoot:
const formEmbed = document.querySelector("form-embed");
const shadow = formEmbed.shadowRoot;
if (shadow) {
const form = shadow.querySelector("form");
form.addEventListener("submit", function () {
dataLayer.push({ event: "shopify_form_submitted" });
});
}
Platforms using this: Shopify Forms
Webhook
An HTTP POST request with a JSON payload is sent on form submission. The most accurate method for server-side tracking:
{
"eventType": "FORM_RESPONSE",
"data": {
"formId": "FORM_ID",
"fields": [
{ "label": "Email", "value": "user@example.com" },
{ "label": "utm_source", "type": "HIDDEN_FIELDS", "value": "google" }
]
}
}
Platforms using this: Tally (free, unlimited), Typeform, HubSpot
Advantage: Browser-independent, unaffected by ad blockers, reliable.
HubSpot _hsq API
A tracking API specific to HubSpot. Page views, user identification, and custom event tracking via the _hsq global object:
var _hsq = (window._hsq = window._hsq || []);
_hsq.push(["trackEvent", { id: "form_submitted", value: 1 }]);
GTM Integration
GTM integration follows a similar pattern across all form platforms:
- Event capture: Via dataLayer, postMessage, or Shadow DOM
- Trigger creation: Custom Event trigger in GTM
- Tag creation: GA4 Event, Google Ads Conversion, or other platform tags
Universal Listener with GTM Custom HTML
If you use multiple form platforms, a single GTM Custom HTML tag can capture them all:
<script>
(function () {
window.addEventListener("message", function (event) {
var trustedOrigins = ["https://tally.so", "https://form.typeform.com"];
if (trustedOrigins.indexOf(event.origin) === -1) return;
var eventName = null;
if (event.data && event.data.event === "Tally.FormSubmitted") {
eventName = "tally_form_submitted";
} else if (event.data && event.data.event === "TypeformSubmit") {
eventName = "typeform_form_submitted";
}
if (eventName) {
window.dataLayer.push({ event: eventName });
}
});
})();
</script>
Origin checking is critical for security. Only add trusted form platform domains to the trusted origins list.
Data Enrichment
The form submission itself isn’t enough. You also need to know which source, campaign, and page it came from.
Hidden Fields
Fields that don’t appear in the form flow but capture values via URL parameters:
https://form-platform.com/form/ID?source=email&campaign=q1-launch
All major form platforms support hidden fields. In Tally and Typeform, URL parameters from the parent page are automatically forwarded to embedded forms.
UTM Parameters
Attributing ad campaign traffic to form conversions:
https://example.com/register?utm_source=google&utm_medium=cpc&utm_campaign=brand
UTM parameters are carried to the form platform via hidden fields. Don’t forget to map these fields when transferring to your CRM.
Server-Side Tracking with Webhooks
In scenarios where client-side tracking is weak (ad blockers, third-party cookie restrictions, consent requirements), webhook-based server-side tracking delivers more reliable results.
Webhook Security
To protect your webhook endpoints:
- Signature verification: Validate the HMAC signature sent by the platform
- Origin control: Use IP whitelisting or token verification
- Idempotency: Prevent duplicate processing of the same event
- Timeout: Return a 2XX response within 10 seconds
Webhook vs Client-Side Tracking
| Criterion | Client-Side (GTM) | Server-Side (Webhook) |
|---|---|---|
| Reliability | Affected by ad blockers | Browser-independent |
| Real-time | Instant | Depends on platform retry intervals |
| Data richness | Browser data available | Form data only |
| Consent | Requires consent | Form submission = consent |
| Setup | GTM container sufficient | Endpoint development required |
The healthiest approach is to use both methods together: GTM for real-time tracking, webhooks for data validation and CRM transfer.
CRM Integration
Three methods are used to transfer form data to CRM:
- Direct integration: Platform’s built-in CRM connection (e.g., Typeform-HubSpot)
- Automation tools: Connection via Zapier, Make, n8n
- Webhook + API: Direct data submission to CRM API
For source information (UTM, hidden fields) to transfer correctly to CRM, field mappings must be complete. Without mapping, data gets labeled as (not set), undefined, or direct.
Platform Guides
Detailed setup and implementation guides for each platform:
- Typeform Conversion Tracking Guide: dataLayer events, GTM integration, cross-domain tracking, hidden fields, HubSpot CRM integration
- Tally Conversion Tracking Guide: postMessage events, webhook setup, hidden fields, UTM tracking, CRM transfer via Zapier/Make
- HubSpot Conversion Tracking Guide: _hsq API, GA4/GTM integration, AJAX form submission, Conversations chat tracking
- Shopify Forms Tracking: Shadow DOM form event capture, localStorage data
- Tracking iFrame Form Events with GTM: postMessage-based iframe tracking, GTM Custom HTML listener, cross-domain considerations
Platform Comparison
| Feature | Typeform | Tally | HubSpot | Shopify Forms |
|---|---|---|---|---|
| dataLayer event | Yes | Pro | _hsq API | Shadow DOM |
| postMessage | Yes | Yes (free) | Chat widget | No |
| Webhook | Yes | Yes (free) | Yes | No |
| Hidden fields | Yes | Yes (free) | Form API | No |
| UTM tracking | Yes | Via hidden fields | UTM + _hsq | No |
| Direct CRM | HubSpot | No | Is the CRM | Shopify CRM |
| Zapier/Make | Yes | Yes | Yes | Yes |
| GTM (embed) | Pro | Pro (code injection) | Pages integration | Theme code |
Checklist
After completing your form conversion tracking setup, verify:
- Does the event appear in GTM dataLayer on form submission?
- Are hidden fields / UTM parameters carried through on form submission?
- Is cross-domain tracking active? Referral exclusion configured?
- Does the new CRM contact show correct source information?
- Is the webhook (if used) delivering successfully? Signature verification active?
- Does consent management meet GDPR/ePrivacy requirements?
- Does the webhook fallback work when ad blockers are active?
- 01 Form platforms communicate events through three methods: dataLayer.push() (Typeform, Tally Pro), postMessage (Tally, iframe forms), and webhooks (server-side)
- 02 Hidden fields and UTM parameters add source, campaign, and user information to form submissions
- 03 Using client-side (GTM) and server-side (webhook) tracking together produces the most reliable results
- 04 Webhook-based tracking is unaffected by ad blockers and third-party cookie restrictions
- 05 Cross-domain tracking, iframe embedding, and consent management must be addressed separately for each form platform
+ What are the core components of form conversion tracking?
Three core components: event capture (an event must fire on form submission), data enrichment (source, campaign, and user information must travel with the event), and transfer (events and data must be delivered to analytics/CRM systems).
+ What is the difference between dataLayer, postMessage, and webhooks?
dataLayer.push() works directly with GTM and requires no additional code. postMessage sends events from iframe embedded forms to the parent page and bypasses third-party cookie restrictions. Webhooks work server-side, are unaffected by ad blockers, but require endpoint development.
+ Should webhook and client-side tracking be used together?
Yes. Client-side tracking via GTM provides real-time conversion reporting. Server-side tracking via webhooks works independently of ad blockers, consent restrictions, and cookie issues. Using both together is the healthiest approach for data validation and CRM transfer.
+ Why is conversion tracking problematic in iframe embedded forms?
When forms inside iframes run on a different domain, third-party cookie restrictions (Safari ITP, Firefox ETP) can prevent GTM/GA4 tracking from working. The postMessage method bypasses this issue because the event is sent to the parent page where the cookie context is preserved.