Enhanced Conversions for Leads — setting it up through Google Tag Manager
When the deal closes not on the site but in the CRM 2 weeks later, ordinary Enhanced Conversions doesn't help. A step-by-step EC for Leads setup through GTM with offline conversion import.
Why you’d even need this
A typical situation for B2B and lead gen:
- Someone clicks your Google ad → submits a “get a quote” form on the site
- For 4 days a sales rep calls them, prepares an offer, sends a proposal
- 2 weeks later the client signs the contract — either at the office or in the CRM, but not on the site
Google Ads only sees the first step — the form submit. Smart Bidding optimizes toward the “lead” as if it were the final goal. But a cheap lead ≠ a closed deal. The algorithm learns to bring lots of cheap leads, of which, say, every 8th one closes. And it looks to you like the ads got efficient, even though you only make money on every eighth one.
This problem used to be solved with Offline Conversion Import (OCI) — you exported a list of closed deals with GCLID from the CRM and uploaded it to Google Ads. But GCLID often gets lost: Safari ITP wipes cookies, blockers strip parameters, UTMs break on redirects. Match rate dropped to 40–50%.
Enhanced Conversions for Leads replaces that approach. Here’s the difference: instead of the unstable GCLID you send Google a hashed email or phone number at the moment the form is filled in. Later, once the deal is closed, you export closed deals from the CRM with that same email. Google matches the two points and restores the chain “click → lead → closed deal”.
EC for Web vs EC for Leads — don’t mix them up
These are two different things. Before you set anything up, make sure it’s the Leads variant you need.
EC for Web EC for Leads The conversion happens on the site (a purchase in an online store) The conversion happens offline (closing in the CRM, a phone call, signing a contract) Extends the existing Google Ads Conversion Tracking tag — adds user data to the same event A separate Google Ads User-Provided Data Eventtag + a later offline conversion importWorks right after setup Only works when you regularly export closed deals E-commerce B2B, lead gen, high ticket, courses, healthcare, construction If you’re in e-commerce — go to the previous article about EC for Web. This one is for lead generation with a long cycle.
When EC for Leads is the right choice
- Lead magnet → nurturing → closing in the CRM
- B2B, where it takes 1–8 weeks from lead to contract
- Agent-based sales: the client submits a form, a rep closes the deal
- High ticket: a course, a medical service, a construction service, a car
In practice this gives you:
- Smart Bidding optimizes toward closed deals, not leads → fewer “junk” leads
- +15–30% attribution accuracy — because matching goes through email/phone, not GCLID
- A transparent funnel LTV — you see from the click to the money, not to the submit
What you need before you start
- ✅ A Google Ads account with a conversion of type Lead (not Purchase, not Website Action — specifically Lead)
- ✅ Google Tag Manager on the site
- ✅ A CRM or a spreadsheet where closed deals are recorded with the client’s email (this is critical — without it EC for Leads makes no sense)
- ✅ On the thank-you page (or right after the form submit) the client’s data sits in the
dataLayer— email required, phone preferred - ✅ Accepted Customer Data Terms in Google Ads — usually done by the account owner, not the agency
How long it takes
The GTM setup itself is 30–40 minutes. Plus you separately need to set up the offline conversion import from the CRM — that’s another 1–2 hours (Google Ads Data Manager or Zapier). You’ll see the first data in Diagnostics in 24–72 hours after ≥20 conversions have gone through the site.
How it works — the scheme in 1 minute
- A user clicks the ad → Conversion Linker in GTM stores the GCLID in a first-party cookie
- The user fills in the form → a
dataLayerwith email/phone is pushed on the thank-you page - The Google Ads User-Provided Data Event tag fires → hashes the email locally and sends it to Google together with the GCLID
- Google temporarily stores the hash on its side and waits for a signal about a closed deal
- For 1–8 weeks the rep works the lead, the deal closes in the CRM
- You export closed deals from the CRM with the fields:
email,conversion_name,conversion_time,conversion_value - You upload them to Google Ads via Data Manager / Zapier / API
- Google matches: “Ah, this email clicked an ad a week ago and submitted a form” → attributes the conversion to the original click
Step 1. Accept the Customer Data Terms in Google Ads
1. Go to Goals (not Conversions!)
Go to Google Ads → the “wrench” icon (Tools and settings) → the Measurement section → Goals.
This is a different section from Conversions. Goals is exactly the place where EC for Leads is activated.
2. Turn on Enhanced Conversions for Leads
In Goals find the Enhanced conversions for leads block. Flip the toggle to ON.
3. Pick the Google Tag Manager method
You’ll be offered a choice of implementation method. Three options:
- Google Tag (gtag.js) — if there’s no GTM
- Google Tag Manager — our option
- Google Ads API — server-side, not the topic of this article
4. Read and accept the Customer Data Terms
Google will show the policy text. You read it → tick the “I agree to the Customer Data Terms” box → Save. The status should change to Accepted.
What you’re confirming: you have a lawful basis to pass user data (consent in the form or legitimate interest), and your privacy policy states that data may be passed to Google in hashed form.
⚠️ If the Terms won’t accept
“If you aren’t able to view terms, it’s because your account conversions are tracked by a manager account” — that means the conversions are managed from an MCC. In that case the Terms have to be accepted from the parent MCC account, not from the child one. Go to MCC → Goals → Accept Terms. This often has to be done by the business owner specifically — an agency without access won’t be able to.
Step 2. Conversion Linker in GTM
Conversion Linker detects the GCLID in the URL and stores it in a first-party cookie on the domain. Without it, Google won’t be able to tie the email hash to a specific ad click.
If you already have Google Ads Conversion Tracking in place, Conversion Linker is most likely already there. Check in Tags. If it isn’t — create it:
1. Create a Conversion Linker tag
- GTM → Tags → New
- Tag Type: Conversion Linker
- Trigger: All Pages
- Name:
Conversion Linker - Save
Step 3. Check the dataLayer after the form submit
Before you dive into the tag settings, make sure the client’s data really is pushed into the dataLayer after the form submit. If it isn’t there, there’s nothing to do further.
1. Open your site in Chrome DevTools
Go to the page with the lead form. F12 (or Cmd+Option+I on Mac) → the Console tab.
2. Fill in a test form
With your real email and phone (you can delete it from the CRM later). Hit Submit.
3. Look at the dataLayer
In the Console type: window.dataLayer → Enter. Find the object with the post-submit event in the response. The desired format:
{
event: "lead_submit",
user_data: {
email_address: "[email protected]",
phone_number: "+380501234567",
address: {
first_name: "Ivan",
last_name: "Petrenko",
postal_code: "01001",
country: "UA"
}
}
}
Your field names may be different — that’s fine. The main thing is that email is mandatory, phone preferred. Memorize the exact keys — you’ll need them in Step 5.
⚠️ If there’s no data in the dataLayer
That means the developer didn’t do the push. Give them this snippet — it has to be inserted into the successful form submit handler (or onto the thank-you page):
window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: "lead_submit", user_data: { email_address: "{{email_from_form}}", phone_number: "{{phone_from_form}}", // E.164 format: +380... address: { first_name: "{{first_name}}", last_name: "{{last_name}}", postal_code: "{{postal_code}}", country: "UA" } } });Important: pass the data in plain text, without hashing. Google will hash it itself in the browser before sending.
Step 4. Choosing how to collect the data
Google offers three options for collecting user data. Here’s the comparison:
| Method | Complexity | When to choose it |
|---|---|---|
| Automatic | Minimal | Simple forms. But phone isn’t collected automatically — only email and address |
| Manual (CSS Selectors) | Medium | When you need phone but have no dev for the dataLayer. You need to know the CSS selectors of the form fields |
| Code (Data Layer) | High (a dev is needed) | Recommended — the most stable option, maximum Match rate |
Below I show Code (Data Layer) — this is the option Google recommends for maximum accuracy. If CSS selectors change during redesigns, Manual “breaks silently” and you won’t notice. Data Layer is stable, if the dev built it.
Step 5. Create the Data Layer Variables
In GTM: Variables → User-Defined Variables → New.
1. A variable for email
- Name:
DLV - user email - Variable Type: Data Layer Variable
- Data Layer Variable Name:
user_data.email_address(or your key) - Save
2. Repeat for all the fields
You create the same kind of variables for:
DLV - user phone→user_data.phone_numberDLV - first name→user_data.address.first_nameDLV - last name→user_data.address.last_nameDLV - postal code→user_data.address.postal_codeDLV - country→user_data.address.country
The more fields you pass, the higher the Match rate at Google. Minimum = email. Ideal = email + phone + address.
Step 6. Create a Trigger for the lead_submit event
1. Custom Event Trigger
- GTM → Triggers → New
- Trigger Type: Custom Event
- Event Name:
lead_submit— exactly as in your dataLayer push - This trigger fires on: All Custom Events
- Name:
CE - lead_submit - Save
Step 7. Create the User-Provided Data Variable
1. A new variable of type User-Provided Data
- Variables → New → Variable Type: User-Provided Data
- Type: Code
2. Bind the fields to the DLV variables
- Email:
{{DLV - user email}} - Phone Number:
{{DLV - user phone}} - First Name:
{{DLV - first name}} - Last Name:
{{DLV - last name}} - Postal Code:
{{DLV - postal code}} - Country:
{{DLV - country}}
Name the variable UPD - Lead User Data → Save.
If the data is already hashed on the site’s side
If for some reason the developer hashes the data before it gets into the dataLayer (happens in paranoid setups), you need to:
- Use Hex-encoded SHA256 (not Base64)
- Prefix the fields:
sha256_email_address,sha256_phone_number,sha256_first_nameand so onpostal_codeandcountry— do not hash, leave them plain textIn 99% of cases you don’t need to do this — let Google do the hashing. Simpler and more stable.
Step 8. The Google Ads User-Provided Data Event Tag
⚠️ This is NOT the Google Ads Conversion Tracking tag
EC for Leads uses a separate tag type —
Google Ads User-Provided Data Event. Don’t confuse it with the existing Conversion Tracking tag you already have. That one works separately and doesn’t need to be touched.
1. Create the tag
- GTM → Tags → New
- Tag Type: Google Ads User-Provided Data Event
- Conversion ID: your Google Ads Customer ID in the format
AW-1234567890(taken from the account address — you strip the hyphens) - User-Provided Data:
{{UPD - Lead User Data}}
2. Bind the trigger
Triggering: CE - lead_submit (created in Step 6).
Name: GAds - EC for Leads → Save.
Step 9. Preview + Publish
1. Preview
Hit Preview at the top right. Tag Assistant will open. Enter your site’s URL → Connect.
2. Walk the funnel with a form submit
In the open Tag Assistant window, go to the page with the form, fill it in, hit Submit. The lead_submit event will appear in Tag Assistant.
3. Check that the tag fired
- Open the
lead_submitevent in Tag Assistant - The Tags Fired section →
GAds - EC for Leadsshould be there - Click on it → the Properties tab
- Check
User-Provided Data: email, phone and the rest of the fields must not be empty
In Preview the data is shown in plain text — that’s normal. Hashing happens later, on the fly, before it’s sent to Google.
4. If the tag is in Tags Not Fired
That means the CE - lead_submit trigger doesn’t match. Check:
- The event name in the dataLayer and in the Custom Event Trigger are completely identical (case matters)
- The Custom Event Trigger is set to All Custom Events, not to some filter
- In the tag, Triggering =
CE - lead_submit, not a random All Pages
5. Publish
Once everything works — go back to GTM → Submit → describe the version (“EC for Leads setup”) → Publish. EC starts working in production.
Step 10. Verification through Network requests
The main quick check is that the data really flies to Google. Preview shows the local picture, while Network shows the actual HTTP requests.
1. Open DevTools → Network
Go to the page with the form (in normal mode, not Preview). F12 → the Network tab. Tick Preserve log.
2. Clear the logs and fill in the form
Hit the “bin” in Network to clear it. Fill in the form → Submit.
3. Find the request to google.com/pagead/form-data/
Filter Network by form-data or pagead. There should be a request to https://google.com/pagead/form-data/....
In the Query String Parameters (or Payload) look for the parameters:
em=— hash emailpn=— hash phonefn=,ln=,pc=,co=— the other fields
If you see non-empty values for these parameters — the data really is flying to Google ✅. If the request is there but the parameters are empty — user_data is breaking somewhere (go back to GTM and check the DLV variables).
Step 11. Offline conversion import (the critical step)
⚠️ Without this step EC for Leads doesn’t work
The whole point of EC for Leads is to tie the form submit (which is in GTM) to the closed deal (which is in the CRM). If you send user data to Google but never export closed deals — Google will never learn about the closed deals, and the attribution won’t update.
There are three ways to import:
Option A. Google Ads Data Manager (recommended)
- Google Ads → Tools → Data Manager → Connect product
- You pick the source: Google Sheets / BigQuery / Salesforce / HubSpot / SFTP / Cloud Storage / HTTP
- You set the schedule (daily / hourly)
- You map the fields: email → User-Provided → Email, value → Conversion Value, timestamp → Conversion Time, currency → Currency
- Save → Test import
The CSV format (for Google Sheets / SFTP):
email,conversion_name,conversion_time,conversion_value,conversion_currency
[email protected],Lead Closed,2026-04-24 14:30:00+00:00,5000,UAH
[email protected],Lead Closed,2026-04-24 15:12:00+00:00,12000,UAH
Option B. Zapier
If your CRM has no native integration with Data Manager — take Zapier. Trigger — “New Closed Deal in CRM”, Action — “Google Ads: Upload Offline Conversion”. You map the fields and launch it. Works instantly.
Option C. Google Ads API
If you have a dev and want full control — through ConversionUploadService + UserIdentifier. Harder, but more flexible. I’d only recommend it if the other two options don’t fit for some reason.
Import frequency — daily, automatically. Google accepts data for the last 90 days. Which means you can launch it and forget about it forever.
Step 12. Checking in Google Ads Diagnostics
In 24–72 hours after publishing (and once ≥20 leads have gone through the form):
- Google Ads → Tools → Conversions → click on your Lead conversion
- The Diagnostics tab
- The Enhanced conversions for leads section
| Metric | What it means | Target |
|---|---|---|
| Data received | % of conversions that arrived with user data from the site | > 70% |
| Match rate | % of conversions where Google managed to match the email to a Google account | > 50% |
| Status | Recording / Not recording | Recording |
If Status = Not recording after 72 hours — the tag isn’t firing at all or the dataLayer is empty. Go back to Preview.
If Data received is low (< 50%) — EC isn’t hooked up on all conversion points (maybe you have several forms — each one has to push user_data).
If Data received is high but Match rate is low — the problem is in the data format (see below).
Common problems
| Problem | Cause | How to fix it |
|---|---|---|
| Status = Not recording | The tag isn’t firing | GTM Preview → check the trigger Custom Event = lead_submit |
| Match rate = 0% | The data is pre-hashed on the site, but GTM treats it as plain text (or the other way round) | Either send plain text (recommended), or add the sha256_ prefix to the fields |
| Match rate low (< 30%) | Wrong phone format | E.164 format: +380501234567 — a plus, no spaces, brackets or hyphens |
| Email doesn’t match | Spaces, upper case | Before the push into the dataLayer: email.toLowerCase().trim() |
| “Terms are not accepted” | The account is under an MCC | Accept the Customer Data Terms from the parent MCC account |
| Conversions aren’t importing | The email in the CRM differs from the email on the site (with a space, a different domain) | Before the upload — normalize: lowercase + trim. Check whether the CRM adds suffixes like +sales |
| EC is silent, but Consent Mode = denied | The user refused cookies | By design. EC doesn’t send data without consent. Check the % granted among your users. |
| Smart bidding doesn’t change behavior | Too little data (< 30 closed deals/month) | EC for Leads needs volume. For small accounts the effect shows up in 2–3 months |
What comes next — after 2–3 weeks
- Match rate stabilizes at 50–80%. Google starts seeing the full picture from click to closed deal.
- Smart Bidding recalibrates — the algorithm notices that some of the “leads” actually become sales and some don’t. It starts pulling in traffic similar to the former.
- You can switch the strategy to
Maximize Conversion Valuewith priority on the “Lead Closed” conversion (rather than just “Lead Submit”). Now Google has the data to do it. - Conversion Value Rules — if you have MQL → SQL → Closed with different probabilities, you can assign different weights. For example, Closed Deal = ×10 against MQL = ×1.
Next up is the mirror of this on Facebook CAPI: you send the hashed email to Facebook after the form submit, and the closed deals through Conversions API Offline Events. The logic is the same.
✅ A short checklist
- Google Ads → Goals → Enhanced conversions for leads = ON, method GTM
- Customer Data Terms accepted (from the right account, if MCC)
- Conversion Linker tag in GTM + an All Pages trigger
- After the form submit — the dataLayer has
user_data.email_address(plain text)- DLV variables created for email, phone, first_name, last_name, postal_code, country
- Custom Event Trigger
CE - lead_submitcreated- User-Provided Data Variable (Code type) created and bound to the DLV
- Google Ads User-Provided Data Event tag created with the right Conversion ID
- Preview verified — the tag fires, user_data isn’t empty
- Network verified —
em=,pn=fly togoogle.com/pagead/form-data/- The GTM container is published
- A regular closed deals import is set up from the CRM via Data Manager / Zapier
- In 24–72 hours — Diagnostics: Status = Recording, Data received > 70%, Match rate > 50%
Google Tag Gateway via Cloudflare — a detailed guide
How to switch Google Tag Gateway on via Cloudflare in 10–15 minutes and recover 11–25% of lost conversions in Google Ads and GA4. Step by step, in English.
Enhanced Conversions — a step-by-step setup for Google Ads and GA4
Form data is already collected on your site but never reaches Google Ads and GA4? Step by step — where to click, what you'll see, how to verify. Setup through Google Tag Manager.
How to set up conversions in Google Ads with Google Tag Manager
A simple and quick manual for setting up conversions.