Why this listener exists
Most Contact Form 7 recipes listen for wpcf7mailsent and push an event with nothing in it. They also ignore the other outcomes CF7 reports: validation errors, spam blocks and mail-server failures.
This listener uses CF7’s own DOM events, reads field values from the event itself, keys fields by your form tags (your-name, your-email) and reports each failure type separately.
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 Contact Form 7’s own success signal, so the event is much closer to a real lead.
How it detects a real submission
cf7_form_success fires on the first of these success paths:
- The
wpcf7mailsentDOM event - The form’s
data-statuschanging tosent - A same-domain thank-you page, matched to data stored at submit
- Page unload inside the submit window with no error showing (inferred)
cf7_form_failed fires on wpcf7invalid, wpcf7spam and wpcf7mailfailed, each with its own detection method, and only 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 |
pagehide |
Success | The page unloaded inside the submit window with no error showing (fallback) |
validation_message |
Failure | A validation message rendered after the visitor interacted |
spam_blocked |
Failure | CF7 blocked the submission as spam |
mail_failed |
Failure | CF7 accepted the form but its mail server failed to send |
error_class |
Failure | The form gained the platform’s error class |
Install it in Google Tag Manager
- Download the GTM tag above, or copy
cf7-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
cf7_form_success. Use the regex^cf7_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
cf7_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
- CF7 internal fields (
_wpcf7*), the honeypot, file uploads and reCAPTCHA tokens are excluded. - A CF7 mail failure means the visitor did submit. Decide whether your reporting counts
mail_failedas a lead. - Debug API:
window._ecCf7Tracker.status().
Events and dataLayer keys
| Key | What it holds |
|---|---|
event |
cf7_form_success, cf7_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
cf7_form_success. - Submit with a required field empty. Expect
cf7_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._ecCf7Tracker.status()in the console to see version, consent state, attached forms and the last event.
Known limitations
- CF7 add-ons that replace the AJAX submit can change which events fire. Test on the live page.
- 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-cf7.1: 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.