Next Action Analyst

2026-07-20

Contact Form 7 Submissions Showing (not set) in GA4? Here's the GTM Fix

You submit a test lead through your Contact Form 7 form, open GA4, and the event either doesn't show up at all or lands in a report as (not set) or Unassigned. Your channel and campaign reports are now useless for judging which campaigns actually drive form fills, which is a problem if you're pulling that CSV into anything downstream.

Why this happens

  1. No GTM trigger is listening for CF7's own event. CF7 fires a native JavaScript event called wpcf7mailsent on successful submission, but GTM does nothing with it unless you build a Custom Event trigger for it. Most setups instead try to trigger on a generic Form Submission trigger, which doesn't reliably fire for CF7's AJAX-based submit.
  2. The GA4 event tag isn't sending source/medium or session data. If the event fires but session parameters like source, medium, or campaign aren't attached (because the event fires after a page reload, or in an iframe, or the session already expired), GA4 falls back to (not set).
  3. The dataLayer push happens before GTM has loaded, so GTM's Custom Event trigger never catches it — common when CF7's script runs early or a caching plugin reorders scripts.
  4. The GA4 Configuration tag fires after the event tag, or on a different trigger, so the event tag sends data without an initialized GA4 client, producing orphaned hits.
  5. Consent Mode or a cookie banner is blocking the GA4 tag from firing at all until consent is granted, and the CF7 submission event happens before that.

Fix, step by step

  1. In WordPress, confirm Contact Form 7 is actually emitting wpcf7mailsent — this is standard behavior since CF7 v5.1+, no extra plugin needed.
  2. Add the dataLayer listener snippet (below) to your theme's functions.php, or via a plugin like Insert Headers and Footers/Code Snippets. This pushes a clean cf7_submit event into the dataLayer whenever any CF7 form successfully mails.
  3. In GTM, create a new Trigger → type Custom Event → Event name: cf7_submit → fires on All Custom Events (or restrict by Form ID variable if you track multiple forms).
  4. Create a new Data Layer Variable in GTM named DLV - Form ID pointing to the key formId, and optionally one for formName, so you can pass form identity into GA4 as event parameters.
  5. Create your GA4 Event tag: Tag type = Google Analytics: GA4 Event, Configuration Tag = your existing GA4 Configuration tag, Event Name = generate_lead or form_submit (pick one and stay consistent), and add Event Parameters mapping form_id and form_name to the Data Layer Variables you just built.
  6. Set the trigger on this tag to the Custom Event trigger from step 3.
  7. Check your GA4 Configuration tag's trigger — it should fire on All Pages (Initialization or Page View), and confirm in GTM's tag sequencing that it fires before the new event tag, not after.
  8. If you use Consent Mode, confirm the GA4 tags have Additional Consent Checks set correctly and that your consent banner isn't blocking analytics_storage before the form event fires in your test session.
  9. Publish the GTM container.

Copy-paste template

// Add to functions.php or a code snippets plugin
add_action('wp_footer', function() {
  ?>
  
   formId
[ ] Variable: DLV - Form Name -> formName
[ ] Tag: GA4 Event, name = generate_lead (or form_submit)
[ ] Tag fires AFTER GA4 Configuration tag
[ ] Event params: form_id = {{DLV - Form ID}}, form_name = {{DLV - Form Name}}
[ ] Container published

How to verify it worked

Open GTM's Preview mode, load the page with your CF7 form, submit a real test entry, and confirm cf7_submit appears in the Summary panel with your GA4 tag firing beneath it. Then switch to GA4's DebugView (Admin → DebugView), repeat the submission, and confirm your event name (e.g. generate_lead) appears in the real-time stream with form_id and form_name populated — not blank, not (not set).

Once this is firing cleanly, your GA4 export will actually attribute leads to the right campaign instead of dumping them into (not set) — which matters if you're feeding that CSV into Next Action Analyst to prioritize which campaigns deserve budget next.

Stop reading dashboards. Start acting on them.
Next Action Analyst turns your campaign export into a prioritized list of changes to make tomorrow — with the numbers that justify each one.