Why this listener exists
WPForms confirms some forms by AJAX, reloads the page for others, and many sites redirect to a thank-you page. A click trigger counts validation errors, and a thank-you trigger counts refreshes.
This listener handles all three WPForms outcomes and keys each field by its visible label, so your dataLayer reads Email instead of wpforms[fields][1].
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 waits for WPForms’s own success signal, so the event is much closer to a real lead.
How it detects a real submission
wpforms_form_success fires on the first of these success paths:
- jQuery
wpformsAjaxSubmitSuccess(AJAX forms) - A
.wpforms-confirmation-containerrendering - A non-AJAX postback confirmation page, matched to data stored at submit
- The form being removed, or the page unloading, after submit (inferred)
wpforms_form_failed fires on wpformsAjaxSubmitFailed or when WPForms renders a field error after the visitor has interacted with the form.
Every event carries ec_detection_method, so you can segment reports by how each lead was detected.
| 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 |
form_removed |
Success | The form was removed or hidden after a valid submit |
pagehide |
Success | The page unloaded inside the submit window with no error showing (fallback) |
error_class |
Failure | The form gained the platform’s error class |
Install it in Google Tag Manager
- Download the GTM tag above, or copy
wpforms-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 and a production config block (debug: false,rawFields: false). - 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
wpforms_form_success. Use the regex^wpforms_form_(success|failed)$if you also want failures. - Create Data Layer Variables for the keys you need, then attach your GA4, Google Ads, Meta or server-side GTM tags to the trigger.
- Open Preview, submit a real test form and confirm one
wpforms_form_success. Then publish.
Retire any old click or thank-you page conversion for the same form, otherwise each lead is counted twice. Do not run this listener and the Universal tag on the same page.
Platform notes
- Field keys come from
.wpforms-field-labeltext plus sub-labels such as First and Last. - The honeypot (
wpforms[hp]) and HTML fields are excluded. - Storage is namespaced (
ec_wpforms_form_data), so it can run next to the other single-platform listeners.
Events and dataLayer keys
| Key | What it holds |
|---|---|
event |
wpforms_form_success, wpforms_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_email, ec_phone |
Email lowercased and validated, phone in E.164. Removed when PII consent is denied |
ec_name, ec_first_name, ec_last_name |
Name, split into first and last where possible |
ec_company, ec_street, ec_city, ec_region, ec_postal_code, ec_country |
Address and company fields for Enhanced Conversions, when the form has them |
form_id, form_platform, form_cta |
Form identifier, platform name and submit button text |
ec_submit_attempt, ec_time_to_submit_s, ec_fast_submit |
Attempt number, time from first input to submit, and a bot hint for very fast submits |
ec_failed_fields |
On failure events: which fields failed validation |
ec_first_source, ec_last_source, ec_click_id |
First and last touch attribution with gclid, gbraid, wbraid, fbclid, msclkid or ttclid |
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_email,ec_phoneand the name and address keys 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
ec_*PII to GA4. Its terms prohibit PII in event parameters. Sendform_id,form_platformandec_detection_methodinstead.
Configuration and consent
Edit the config block at the top of the tag:
| Key | Default | Notes |
|---|---|---|
debug |
false |
Console logs. Testing only |
hashPii |
false |
Adds ec_email_sha256 and ec_phone_sha256 (needs HTTPS) |
rawFields |
false |
Leaves ec_raw_fields out of the payload |
dedupe |
true |
Suppresses a repeat success for the same form and person within 5 minutes |
strictSuccess |
false |
true drops inferred successes (pagehide, form_removed) |
defaultCountryCode |
'1' |
Phone E.164 fallback. A country field on the form takes priority |
countryCodeField |
'' |
Exact field name that holds an ISO2 or dial code |
redactFields |
[] |
Extra field names to strip. Password, card, SSN and IBAN fields are always stripped |
The tracker auto-detects Google Consent Mode v2, OneTrust and Cookiebot. With analytics consent denied, nothing fires and nothing is stored, and events raised in the meantime are replayed if consent is granted. With PII consent denied, events fire without contact data. 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
wpforms_form_success. - Submit with a required field empty. Expect
wpforms_form_failedand no success event. - Fix the error and resubmit. Expect one success.
- Refresh the confirmation or thank-you page. Expect nothing new.
- Run
window._ecFormTrackers.wpforms.status()in the console to see version, consent state, attached forms and the last event.
Known limitations
- Without jQuery, AJAX success relies on the confirmation container render.
- A redirect to a different domain cannot use same-domain recall.
- Bot or spam submissions that pass the platform’s own validation still count as success.
- Phone normalization is best-effort.
Changelog
- 3.3.0: first public standalone release, cut from the Universal tracker v3.3.0. Same core: consent gate with replay, PII redaction, optional SHA-256 hashing, 5-minute dedupe,
ec_event_id, attribution, failure diagnostics andstrictSuccess.