Quick Answer
Offline conversion tracking gets hard because most setups turn into a chain of separate tools stitched together by hand. CallRail feeds Zapier, Zapier feeds a spreadsheet, someone updates a column when a lead closes, then that sheet gets reformatted and imported into Google Ads. The fix is not a smarter spreadsheet. It's picking one system of record for lead status and automating the push to Google Ads with a captured GCLID, so nothing depends on a person remembering to update a sheet.
The Offline Conversion Mess Most PPC Managers End Up With
If you manage Google Ads for lead gen clients, you've probably built some version of this stack without meaning to. CallRail logs the call. Zapier pushes it into a spreadsheet. You add a column to mark the lead qualified. A second sheet gets formatted for the Google Ads offline conversion import. Then you do the whole thing again for contact form leads, because the form tool and the call tool don't talk to each other.
This happens by default whenever calls, forms, and closed deals live in three different places and Google Ads only wants one clean file to import. People running this setup across several client accounts describe spending real hours every week just keeping the sheets current, and every added tool in the chain is one more place a lead can silently drop out without anyone noticing until the numbers look off.
That last part is the expensive one. A tracking gap doesn't throw an error. It just quietly starves your bidding algorithm of the data it needs, and the campaign looks like it's underperforming when the real problem is that half your closed leads never made it back to Google Ads.
Why This Happens
Google Ads assumes you have one clean CRM feeding it offline conversion data on a predictable schedule. Most accounts don't work that way. Some clients run HubSpot, some run Salesforce, some run nothing more than a shared inbox and a sales rep's memory. If you manage more than one account, you're not maintaining one tracking setup, you're maintaining a different one for every client, because every client's stack is different.
That's the actual root of the "why is this so hard" feeling. Offline conversion tracking isn't technically complicated once the data reaching Google is clean. People who've built working versions of this describe the upload itself as close to trivial. What actually eats the time is producing consistent, trustworthy data out of a mess of CRMs, call systems, and form tools before any of it gets near Google Ads.
How to Fix It
Step 1: Capture the click ID before anything else
Every offline conversion has to trace back to a click. That means grabbing the GCLID (and GBRAID or WBRAID for the traffic GCLID misses) the moment someone lands on your site, before they fill out a form or call. If you're not capturing click IDs at the point of the visit, nothing downstream will match correctly no matter how good your CRM data looks later.
Step 2: Pick one system of record for lead status
This is the step most setups skip. Choose a single place where lead status lives for each client, whether that's a real CRM like HubSpot or Salesforce, or a lightweight tool built for exactly this job, like FormTrackr or WhatConverts. Which tool you pick matters less than having one source of truth per client instead of a sheet that only you remember to update.
Step 3: Automate the push back to Google Ads
Once lead status changes in one place, that change should flow to Google Ads on its own, through the platform's native integration, a webhook, or the offline conversion API. Manual CSV imports are where most tracking gaps happen, because they depend on someone remembering to run them on schedule. Full disclosure, this is exactly the loop FormTrackr was built to close (we built it), tying the click ID to the call or form fill and sending the conversion back to Google Ads automatically. WhatConverts solves the same problem from a different angle if you'd rather go that route.
Step 4: Set one clear rule for what counts as a conversion
Decide, in writing, what qualifies as a reportable conversion for each client. Is it a booked appointment? A sale? A lead marked qualified by sales? Without a written rule, every person touching the CRM makes their own call, and your conversion data quietly drifts depending on who updated the record that week.
None of these four steps require a developer. They require picking one system instead of stitching together five, and building the push-back in so nobody has to run it by hand.
If you want the full setup walked through step by step, including how to capture click IDs correctly and where offline conversion imports commonly break, grab the free OCT Setup Checklist. It's built directly from working setups, not Google's documentation, which is exactly the part advertisers say is missing.
FAQ
Do I need a full CRM to do offline conversion tracking?
No. A CRM makes it easier because it gives you one place to track lead status, but it's not required. A tool built specifically for call and form tracking, like FormTrackr or WhatConverts, can serve as your system of record if a client doesn't have a CRM.
Is GCLID import better than enhanced conversions for leads?
They solve different problems, not competing versions of the same one. GCLID import sends Google the outcome of leads you already know closed. Enhanced conversions for leads helps recover conversion data lost to cookie and privacy restrictions. Most accounts benefit from running both.
Why do my offline conversions stop matching partway through the funnel?
Usually because the click ID gets dropped somewhere between the click and the CRM entry, often when a lead moves between tools that don't pass it along. Check that the GCLID is captured on the landing page and stored with the lead record before it ever reaches your CRM or spreadsheet.
How often should offline conversions be uploaded to Google Ads?
As close to real time as your system allows. Delayed uploads, especially anything past a couple of weeks, hurt Smart Bidding because the algorithm is optimizing off conversion data it hasn't seen yet. Automating the push, instead of batching a manual import weekly, is what actually fixes this.