Offline conversion tracking closes the gap between what ad platforms see (a form fill) and what you actually want (qualified pipeline and revenue). You capture click IDs and contact data when a lead submits, store them in your CRM, and send qualified stages and revenue back to Google Ads and Meta so their bidding learns which clicks become customers. This covers the CRM side; web-side Conversions API setup is a separate job.
Why optimizing to form fills misleads algorithms
Smart Bidding and Meta’s delivery system find more of whatever conversion you count. Count form submissions and they find form fillers, and the cheapest ones are often students, job seekers, vendors, competitors and bots.
A hypothetical example with round numbers: Campaign A produces 100 leads at $50 each, and Campaign B produces 40 leads at $100 each. On form fills alone, A looks twice as efficient, so the algorithm shifts budget toward it. Now add the CRM: A’s leads turn into 2 opportunities and B’s into 8. Cost per opportunity is $2,500 for A and $500 for B. The platform optimized the wrong way while reporting success.
Broad automation makes this worse: Performance Max, broad match and Advantage+ audiences excel at finding cheap conversions, including where junk leads are plentiful. CRM outcomes give that reach something worth finding, and they’re the foundation of running Google Ads for B2B SaaS on pipeline instead of clicks.
Capturing click IDs and user data at submission
The whole loop depends on the form submission. Identifiers missed there can’t be matched later. Capture these on every lead form:
| Field | Source | Used by |
|---|---|---|
gclid | URL parameter from Google Ads auto-tagging | Google offline imports |
gbraid / wbraid | URL parameters on some iOS traffic that arrives without a GCLID | Google offline imports |
fbc | The _fbc cookie, or built from the fbclid URL parameter | Meta Conversions API |
fbp | The _fbp browser ID cookie set by the Meta pixel | Meta Conversions API |
| Email and phone | The form itself | Enhanced conversions for leads, Meta matching |
| UTMs and landing page | URL on first landing | Reporting and QA |
| Submission timestamp | Server time, with time zone | Upload timing |
| Consent status | Your consent tool | Filtering before upload |
The mechanics:
- Turn on auto-tagging in Google Ads. Without it, there’s no GCLID to capture.
- Persist click IDs on landing. A short script stores the URL parameters in a first-party cookie, because most people don’t convert on the first page. Some browsers cap the life of JavaScript-set cookies, so a server-set cookie survives longer.
- Write them into hidden fields at submission, with the UTMs. A UTM naming convention keeps those usable for QA.
- Turn on enhanced conversions for leads in Google Ads so the Google tag also sends hashed contact data from the form, letting Google match later uploads by email when the GCLID is missing.
Test with a real click on a live ad, then check the record in the CRM, not the form tool. Integrations often drop hidden fields nobody mapped.
Storing it in your CRM
Create dedicated fields and treat them as system fields that reps can’t edit:
- Click ID fields on the contact or lead: GCLID, GBRAID, WBRAID, FBC, FBP, plus the Meta lead ID for instant form leads
- Original submission timestamp and consent status
- The same click IDs copied onto the deal or opportunity when it’s created, since revenue lives on the deal
- A “sent” timestamp per stage and per platform, so a re-run never uploads the same conversion twice
Two rules prevent most of the messes I untangle in audits. First, don’t overwrite a stored click ID when a known contact submits another form; add a separate “latest click” field if you want it. Second, keep raw email and phone in the CRM and hash at send time, because stored hashes go stale when someone fixes a typo.
Google Ads has a native Salesforce import, and HubSpot can sync lifecycle stage changes to both Google Ads and Meta. Check what your CRM already supports before building a custom pipeline.
Google Ads offline imports and enhanced conversions for leads
In Google Ads, create one conversion action per CRM stage you’ll send, using the import option for conversions from clicks via CRM, files or other data sources. Data can arrive via scheduled file or Google Sheet uploads, the Google Ads API, or a CRM connector.
Each conversion row needs:
- The click identifier (GCLID, GBRAID or WBRAID), hashed email and phone, or both
- The conversion action name, exactly as it appears in Google Ads
- The conversion time with time zone, which must be later than the click
- Value and currency
Enhanced conversions for leads matches on hashed contact data instead of, or alongside, the click ID. Normalize first: trim and lowercase emails, format phone numbers in E.164 with the country code, then hash with SHA-256. Send both GCLID and email when you have them; the email rescues conversions where the ID was lost.
Mind the window. A conversion action’s click-through window maxes out at 90 days, and later conversions aren’t credited to the click. If your sales cycle runs longer, closed-won imports cover only your fastest deals, so earlier stages carry the bidding. Google also accepts adjustments, so you can restate a changed deal value or retract a junk lead.
Upload daily, and judge a new setup on a week of data, not the first morning.
Meta Conversions API for CRM events
For Meta, CRM stages go through the Conversions API as server events. Each event carries:
event_name: the stage, such as a customQualifiedLeador the standardPurchasefor closed revenueevent_time: when the stage changed, as a Unix timestampaction_source:system_generated, the value Meta’s CRM integration specifies for stage eventsuser_data: hashed email and phone, plusfbc,fbpand the lead ID when you have themcustom_data: value and currency
Hash with the same normalization as Google. Events Manager shows an Event Match Quality score per event, and more identifiers generally mean better matching. For Meta instant form leads, the lead ID is your strongest identifier, and it lets Meta optimize lead campaigns toward leads that reach a chosen CRM stage.
Two practical constraints. Meta only accepts events with recent timestamps, so send stage changes daily rather than in a monthly batch. And Meta’s click attribution windows are measured in days, not months, so a deal that closes two months after the click usually won’t be credited. Meta learns best from stages leads reach soon after they submit.
CRM events come only from your server, so there’s no pixel event to deduplicate against. Sent flags guard against double counting, and a stable event_id per lead and stage makes repeats easy to spot.
Choosing which stages and values to send
Send two to four stages, not every lifecycle step. A typical B2B set:
| Stage | Google Ads role | Meta role | Value sent |
|---|---|---|---|
| Lead (form fill) | Secondary | Web event, not from CRM | None or small |
| Qualified lead (MQL or SQL) | Primary once volume allows | Optimization event for lead campaigns | Expected value |
| Opportunity or meeting held | Primary or secondary | Custom event | Expected value |
| Closed-won | Secondary, for reporting | Purchase | Actual first-year contract value |
Pick the primary with a volume test: the deepest stage that still produces enough conversions for bidding to learn. Meta’s guidance is roughly 50 optimization events per ad set per week to exit the learning phase. For Google, a common working minimum is around 30 conversions a month per campaign. If the deep stage is too thin, bid on the stage above it and move down later.
Set values from your own funnel: expected value = stage-to-close rate × average first-year contract value. Hypothetically, with a $24,000 average first-year contract, a 20% SQL-to-close rate makes each SQL worth $4,800, and a 5% qualified-lead-to-close rate makes each qualified lead worth $1,200.
Value rules I hold every setup to:
- One value logic for both platforms, computed in the CRM or pipeline, so Google and Meta compete on the same numbers.
- One primary stage per campaign goal. If several stages are primary, bidding sums their values and counts the same deal twice.
- Recalculate stage values quarterly from actual close rates, not once at setup.
- First-year value, one currency. Skip lifetime value estimates you can’t verify yet.
- Disqualified leads get nothing. Retract them in Google if they were already uploaded.
Validating the data loop
A loop that silently drops conversions teaches bidding a skewed picture. Check before changing bidding, then weekly:
- Capture rate: the share of paid leads in the CRM with a click ID or contact data. Gaps point to broken form mapping, redirects stripping parameters or cookie issues.
- Upload success: Google Ads diagnostics and Meta Events Manager show events received, with errors reviewed: unmatched click IDs, conversion times before the click, wrong action names, time zone offsets.
- Match quality: Meta’s Event Match Quality and Google’s enhanced conversions diagnostics are stable or improving.
- Reconciliation: CRM qualified leads from paid sources vs each platform’s reported conversions for the same period. Expect a stable gap; a sudden shift means something broke.
- No duplicates: sent flags update after every successful upload, and a re-run uploads zero rows.
- Test path: Meta’s test events tool and a small manual Google upload before any new stage goes live.
- Observation period: a new stage runs as a secondary conversion for two to four weeks before bidding switches to it.
When the numbers hold, switch bidding one campaign at a time and expect a relearning period. It’s usually the first thing I build in marketing attribution work, since every later budget decision depends on it.
Get it built
If your ad platforms report cheap leads while sales complains about lead quality, a Growth Audit maps where the loop breaks and what to fix first. It’s $1,500 fixed and credited if we continue. See pricing or get in touch.