Why this listener exists
The popular Gravity Forms recipes listen to gform_confirmation_loaded and stop there. That event only fires for AJAX forms, so forms that reload the page or redirect to a thank-you page go untracked, and none of them hand you the email or phone for Enhanced Conversions.
This listener covers all three Gravity Forms outcomes (AJAX confirmation, postback confirmation page and thank-you redirect), skips the honeypot field, and captures the lead’s contact details in the format Google and Meta expect.
Most form tracking fires on a button click or a thank-you page view. Clicks count validation failures and repeat clicks. Thank-you page views count refreshes, back-button visits and bookmarked links. This listener watches what Gravity Forms actually renders, so the event is much closer to a real lead.
How it detects a real submission
gf_form_success fires on the first of these success paths:
- The
gform_confirmation_loadedevent - A
.gform_confirmation_wrapperrendering on the page - A postback confirmation page where the wrapper is present and the form is gone
- A same-domain thank-you page, matched to recent form data stored at submit
gf_form_failed fires when Gravity Forms renders a validation message after the visitor has interacted with the form, so empty rage-clicks are ignored.
Every event carries ec_detection_method, so you can segment reports by how each lead was detected and see which signals your site actually uses.
| ec_detection_method | Event | What it means |
|---|---|---|
confirmation_event |
Success | The platform’s own success event fired |
confirmation_class |
Success | The form gained the platform’s success class |
stored_recall |
Success | Same-domain thank-you page, matched to data stored at submit |
validation_message |
Failure | A validation message rendered after the visitor interacted |
Install it in Google Tag Manager
- Download the GTM tag above, or copy
gravityforms-form-tracker.gtm.htmlfrom the GitHub repository. - In GTM, create a Custom HTML tag and paste the file in. It already includes the
<script>wrapper. - Trigger it on All Pages (Page View), or on Consent Initialization if your consent setup needs the tracker available early.
- Create a Custom Event trigger for
gf_form_success. Use the regex^gf_form_(success|failed)$if you also want failures. - Create Data Layer Variables for the keys you need (table below), then attach your GA4, Google Ads, Meta or server-side GTM tags to the trigger.
- Open Preview, submit a real test form and confirm
gf_form_successappears with the expectedec_detection_method. Then publish.
Retire any old click or thank-you page conversion for the same form, otherwise each lead is counted twice.
Events and dataLayer keys
| Key | What it holds |
|---|---|
event |
gf_form_success, gf_form_failed, ec_form_interaction (first real input) or ec_tracker_initialized (script loaded) |
ec_event_id |
UUID per event. Pass it to Meta as the event ID so browser and CAPI events deduplicate |
ec_detection_method |
Which success or failure path fired (see above) |
ec_tracker_version |
Script version, useful when you audit containers later |
ec_email, ec_phone, ec_name |
Normalized PII from the form (phone in E.164 where possible). Removed when PII consent is denied |
ec_email_sha256, ec_phone_sha256 |
SHA-256 hashes, added when hashPii: true (needs HTTPS) |
ec_raw_fields |
Every captured field with a _bucket classification. Keep it out of GA4 |
form_id, form_platform |
Form identifier and platform name |
form_position, form_index |
Where the form sits on the page (top, middle, footer) and its order, for multi-form pages |
form_url, form_cta |
Page URL and the submit button text |
ec_pii_redacted |
true when PII consent is denied and PII keys were stripped |
Using it for Enhanced Conversions and Meta CAPI
- Google Ads Enhanced Conversions: map
ec_emailandec_phone(or the_sha256versions) into a User-Provided Data variable on your conversion tag. - Meta Pixel and CAPI: send
ec_event_idas the event ID on both the browser and server event so Meta counts the lead once. - GA4: do not send raw
ec_*PII orec_raw_fieldsto GA4. Its terms prohibit PII in event parameters. Sendform_id,form_platformandec_detection_methodinstead. - Duplicates: with
dedupe: true(the default), a repeat success for the same form, email or phone within 5 minutes is suppressed.
Configuration and consent
Set window.EC_FORM_CONFIG before the tag loads to change the defaults:
window.EC_FORM_CONFIG = {
debug: false, // [EC] console logs, Preview only
hashPii: false, // add ec_email_sha256 / ec_phone_sha256
dedupe: true, // suppress repeat success within 5 min
ttlMs: 600000, // freshness window for thank-you recall
defaultCountryCode: "1", // phone normalization fallback
extraThankYouPatterns: [] // extra thank-you URL substrings
};
The tracker checks consent before it fires or writes storage. It auto-detects Google Consent Mode v2, OneTrust and Cookiebot. With analytics consent denied, nothing fires and nothing is stored. With PII consent denied, events still fire but email, phone, name and hashes are removed. Any other CMP can be wired in with window.EC_FORM_CONSENT_CHECK. If no CMP or override is found, the tracker assumes full consent, so match it to your own legal requirements.
Test it before you publish
- Submit a valid form. Expect one
gf_form_success. - Submit with a required field empty. Expect
gf_form_failedand no success event. - Fix the error and resubmit. Expect one success (recovery is allowed).
- Refresh the confirmation or thank-you page. Expect nothing new.
- If the page has two forms, submit each and check
form_positionandform_index. - Run
window._ecFormTrackers.gf.status()in the console to see version, consent state, attached forms and the last event.
Troubleshooting
- Seeing two success events per submit from an old setup? Remove any confirmation-message
dataLayer.pushsnippets added for earlier recipes. - No
ec_tracker_initializedin Preview? The tag is not firing on that page, or consent for analytics is denied. - Events in Preview but not live? Make sure the container is published, and that a cache or script optimizer (for example WP Rocket delay JS or Cloudflare Rocket Loader) is not deferring GTM.
- Set
debug: trueinEC_FORM_CONFIGto see[EC]logs while testing.
Known limitations
- Gravity Forms add-ons and themes can change the markup. Test on the live page before publishing.
- A redirect to a different domain cannot use same-domain recall.
- Multi-form pages are scoped by Gravity Forms form ID where available, with a single generic fallback.
- Bot or spam submissions that pass the platform’s own validation still count as success.
- Phone normalization is best-effort. The raw value stays in
ec_raw_fieldswhen PII consent allows it.
Changelog
- 3.0.1: first public standalone release. Confirmation-render detection, success and failure events, consent gate (Consent Mode v2, OneTrust, Cookiebot), PII redaction, optional SHA-256 hashing, 5-minute dedupe,
ec_event_id, thank-you recall and thestatus()debug API.