← all writing
Business By Jesse Moraga · Nov 2, 2026 · 11 min read

Closing the Loop: Sending Paid Invoices Back to Google Ads as Conversions

Your ad account optimizes for whatever you tell it is a win, so tell it the truth: the paid invoice, not the form fill.

A form fill is a guess. Somebody typed their name into a box, which is nice, but nobody has handed you money yet and the algorithm bidding your budget has no idea whether that person was a real job or a tire kicker who found you at 11pm and ghosted. So every bid it makes is built on a lie you fed it. That's the whole problem, and the fix is older and less glamorous than people expect: you send the sale back.

In this post:

1. Why it breaks

Here's the shape of the failure. You run search ads. Somebody clicks, lands on your page, fills out the form. Your tag fires a conversion. Google records it, learns from it, and goes looking for more people who behave like that person.

Except "that person" is a category that includes four very different humans. The one who books and pays. The one who wanted a number for an insurance claim and was never going to hire anybody. The one who is price shopping six companies and will take the cheapest. And the bot that fills out forms for fun. Your ad account weights all four identically, because from where it's standing they did the exact same thing: they submitted a form.

So what happens over weeks is a slow drift. The keywords that produce a flood of cheap, low-intent form fills start looking fantastic on the dashboard. Cost per conversion drops. You feel good. Meanwhile the keywords that produce three leads a week, two of which turn into real paying work, look expensive and get starved. The algorithm is doing exactly what you asked. You just asked for the wrong thing.

I'm not the type to blame the tool here. Smart Bidding is genuinely good at finding more of what you point it at. The problem is that most small businesses point it at the cheapest possible signal because that's the one that's easy to fire from a thank-you page, and then they complain that Google wasted their money. Google didn't waste it. You told it that a form fill was the finish line.

And honestly, the common advice makes it worse. Half the agency playbooks out there say to count a phone call over 60 seconds as a conversion, or a page view of the contact page, or a chat open. Those are proxies stacked on proxies. Every layer of proxy between the signal and the money is another place for the account to get confused. The money is the only thing that isn't a proxy.

2. The mechanism

The mechanism is called offline conversion import, and the idea is simple enough that I think people skip it because it sounds too plain. Google hands you an identifier when someone clicks your ad. You store that identifier with the lead. Later, when that lead becomes revenue, you send the identifier back along with the conversion. Google matches it to the original click and credits the right keyword, the right ad, the right device, the right time of day.

The identifier is the GCLID, the Google Click Identifier, appended to your landing page URL as a query parameter when auto-tagging is on. It's a long opaque string. It means nothing to you and everything to Google. There's a parallel one for Microsoft, and the newer Enhanced Conversions for Leads flow lets you match on hashed email instead if you'd rather not babysit a URL parameter, but the logic is identical either way. Google's own documentation on importing offline conversions from clicks walks the setup, and I'd read it before you build anything, because the conversion action has to exist on their side before your upload has anywhere to land.

The part that matters conceptually: this turns your ad account from an open loop into a closed one.

OPEN LOOP (what most people run)

  Ad click ──► Landing page ──► Form fill ──► [tag fires]
                                                  │
                                                  ▼
                                        Google learns: "WIN"
                                                  │
  ... job gets quoted, lost, never booked         │
      invoice never exists                        │
      Google never hears about it  ───X           ▼
                                        Bids harder on this keyword


CLOSED LOOP (what you want)

  Ad click
    │  gclid=Cj0KCQ...
    ▼
  Landing page ──► Intake form
                      │  stores: gclid + timestamp + contact
                      ▼
                 Lead record ──► Quote ──► Job ──► INVOICE
                      │                              │
                      │                              │ marked PAID
                      │                              ▼
                      │                      Webhook fires
                      │                              │
                      │                   ┌──────────┴──────────┐
                      │                   │ fired flag on       │
                      │                   │ record already set? │
                      │                   └──────────┬──────────┘
                      │                     no │     │ yes
                      │                        ▼     ▼
                      │                     send   drop it
                      │                        │
                      └────── gclid ───────────┤
                                               ▼
                                   Offline conversion upload
                                        (gclid + value + time)
                                               │
                                               ▼
                                   Google learns: "REAL MONEY"
                                               │
                                               ▼
                              Bids on keywords that produce invoices

Look at the two arrows leaving the lead record. The gclid goes in at intake and comes back out at payment, and the only thing between them is the fired flag that decides whether this upload has already happened.

That gap between the two arrows is the whole game. In a small services business it might be a day. It might be three weeks, because the customer got a quote, thought about it, called their spouse, called you back, scheduled, got the work done, then paid net fifteen. Everything in between is just your business operating normally. The gclid sits there patiently in a database column waiting to be useful.

Two implementation details that bite people. First, there's a lookback window on the match, so if your sales cycle runs long you can age out of it and Google will quietly reject the upload. Second, the conversion time you send has to be after the click time, which sounds obvious until you're backfilling historical data with sloppy timestamps and every row bounces.

3. How I run it

I own a field services company in California and I built the back office for it myself: intake, quoting, invoicing, follow-up, the phone assistant, field updates. So I'm not describing a theory I read about. I'm describing the thing that pays my mortgage.

Capture happens at intake, not later

Intake submissions in my system are attribution-matched to the click that brought them. That's the first half. When a form comes in, the record that gets created carries the click context with it, and that context rides along with that customer through every stage after. It isn't a separate analytics table I have to join against later and pray the timestamps line up. It's a field on the lead.

This is the part people get wrong most often, and it's not a sophisticated mistake, it's just a sequencing mistake. They decide they want offline conversions six months into running ads, and then they go looking for the click data for leads that already came in, and it isn't there, because nobody wrote it down at the moment it existed. The gclid is only in the URL for one page load. If you don't grab it right then and stash it on the record, it's gone. You can't reconstruct it from a spreadsheet of names.

So the rule I run by: capture is a first-class part of intake, same as the phone number. If the form can't save the click context, the form isn't done.

The sale fires at paid, not at booked

Every paid invoice in my system fires a purchase event. Not quoted. Not scheduled. Not "job completed." Paid. Money in the account, invoice marked closed, event fires.

I picked that boundary on purpose and I'd defend it. Booked work falls through. People cancel, reschedule into oblivion, dispute, decide they want somebody else. If I counted booked jobs as conversions I'd be feeding my ad account a number that's optimistic by some unknown percentage that changes month to month, and then I'd be tuning bids against a fiction. Paid is the one state in the whole pipeline that can't be revised by someone changing their mind. It's the only honest one.

The downside is latency, and it's real. Google doesn't hear about a job until the money clears, which means the bidding algorithm is always learning from a slightly stale picture of my business. I'll take it. Slow truth beats fast fiction, especially when the fiction is the thing deciding how to spend my ad budget.

The idempotency flag, which is the unglamorous part that actually matters

Here's the detail I'd put on a poster if I made posters. The purchase event in my system is idempotent on a "fired" flag stored directly on the record. Before anything goes out, the handler checks that flag. If it's set, the handler stops. If it isn't, the event goes out and the flag gets set.

Why this exists: webhooks retry. That's not a bug in your payment processor, that's correct behavior. If the processor sends you a payment notification and doesn't get a clean response fast enough, maybe your server was restarting, maybe the network hiccuped, maybe your handler took too long doing something unrelated, it will send that notification again. Sometimes several times. A webhook that arrives twice is not an edge case, it's Tuesday.

Without the flag, a retried webhook counts the sale twice. And the failure mode is nasty because it's silent. Nothing errors. Nothing logs red. Your conversion numbers are just quietly inflated by some unpredictable amount, and worse, they're inflated unevenly, because retries cluster around whatever was slow or flaky that day. So you don't just have wrong numbers, you have wrong numbers with a bias you can't characterize. You'll never find it by staring at the dashboard. It just looks like a good week.

Storing the flag on the record itself, rather than in some separate dedupe cache with a TTL, means the guarantee is permanent and it lives where the thing it's protecting lives. If I pull up that invoice two years from now I can see that its conversion already went out. No expiry, no second system to keep alive, no cache to warm.

What the ad account learns

Because intake carries attribution and payment carries the event, the ad account ends up learning which keywords produced paying work rather than which keywords produced form fills. Those are not the same list. They have never been the same list in any account I've looked at.

Some queries bring volume and almost no revenue. Some bring one lead a week that turns into real work nearly every time. On form-fill optimization, the first group wins and eats the budget. On revenue optimization, the ranking inverts. Same spend, same ads, completely different allocation, and the only thing that changed is which signal the account is chasing.

That's the thing I want anyone reading this to take away. This is not an analytics upgrade. This is changing what the machine is hunting for.

4. Tradeoffs

This costs you real engineering, and if you don't have the volume to support a bidding algorithm it's wasted engineering. Smart Bidding wants a meaningful number of conversions to learn from, and if your business closes a handful of jobs a month, switching from form fills to paid invoices takes your conversion count from "barely enough" to "statistically nothing," and the algorithm will thrash. In that situation you're better off keeping form fills as the bidding signal, importing paid invoices as a secondary conversion action you only observe, and looking at the keyword report with your own eyes once a month. It's less elegant. It works fine at small scale. The other honest cost: you now have a data pipeline that can break quietly. The gclid column can stop getting populated because somebody redesigned the landing page and dropped the hidden field, and nothing will alert you, your conversions will just taper off into nothing while your ads keep running. Long sales cycles can blow past the match window. Offline attribution also means the customer has to come back through a channel you can tie to that record, so if half your work arrives by phone call and you're not capturing click context on those too, you're closing the loop on half your business and your data has a hole in exactly the shape of your best customers. Build the monitoring before you trust the numbers.

Takeaway

One line: your ad account will faithfully buy more of whatever you call a win, so make the win a paid invoice.

Jesse

growth-as-a-service

I build with AI so small businesses can take on the giants. Let me build yours.

Visit Art3ry → art3ry.com