Case study · Tracking and attribution

Telling the ad platforms what actually happened

Two server-side integrations for two different problems. Meta's Conversions API on my own funnel, where the browser and the server both report the same event and have to be deduplicated. And closed deals pushed from HubSpot back to Google Ads, so bidding optimises on revenue instead of form fills.

Meta Conversions APIGoogle Ads offline conversionsHubSpotEvent deduplication
2platforms, two different mechanisms
1event ID, which is what makes dedup work
Own spendthe Meta build runs on my own funnel
Closed dealsnot form fills, sent back to Google

Problem

The platform optimises towards whatever you tell it about.

An ad platform bids using the conversions it can see. Tell it about form fills and it will get very good at finding people who fill in forms. Some of those people are job seekers, suppliers, or students. The platform is doing exactly what you asked.

Two things break the feedback loop. Browser tracking misses events, because of ad blockers, privacy settings, and the shrinking life of third-party cookies. And the events the browser does catch are the wrong ones: a form fill happens in seconds, while the thing you actually care about happens weeks later, in a CRM the ad platform has never heard of.

Two problems, two mechanisms

Two tracking lanes. Meta receives the same event from browser and server with a shared event ID for deduplication. Google Ads receives the closed deal weeks later, matched by stored click ID. LANE 1 — META, SAME MOMENT, TWO SENDERS Browser pixelfires, or is blocked My serveralways fires Shared event IDON BOTH SENDS Metacounts it once no ID meansit counts twice LANE 2 — GOOGLE, WEEKS APART Clickclick ID captured CRM contactID stored, kept Deal closesweeks later, with a value Sent to GoogleWITH THE REVENUE bidding now moves on deals, not on form fills
Meta is a deduplication problem: two senders, one event, at the same moment. Google is a memory problem: one event, reported weeks after the click that caused it.

Lane one

Meta: the same event arrives twice on purpose.

This runs on LadderFunnel, my own paid acquisition product, on the funnel behind go.movetospain.es. The spend is mine, which is the part I like about it as a reference. Nobody was going to cover for me if the numbers went wrong.

The browser pixel still fires. The server also sends the same conversion straight to Meta, and the server send is the reliable one: no ad blocker, no cookie policy, no closed tab halfway through. So the same conversion is reported twice on purpose, because you want the coverage of the server send and the extra signal the browser can attach.

Which means Meta has to be told the two are one event. Both sends carry the same event ID. Meta sees the pair, matches the IDs, and counts one conversion.

Get that wrong and it fails quietly, which is the dangerous part:

  • No event ID at all and every conversion counts twice. Your reported cost per acquisition halves overnight and looks like a win.
  • Mismatched IDs, because the server generates its own instead of using the one the browser already made, and you get the same doubling with none of the obvious cause.
  • Different event names across the two senders and Meta will not even try to match them.

The ID has to be created once, at the moment the conversion happens, and carried to both destinations. It is easy to describe and easy to get subtly wrong, which is why interviewers ask about it.

Lane two

Google: the conversion happens long after the click.

This one is at LXT, and the problem is different. In B2B the form fill is not the conversion. It is the start of a conversation that becomes a deal, or does not, some weeks later. Google Ads never sees that ending, so left alone it optimises towards whatever produces the most form fills.

The fix is to remember the click and report back when you know the answer:

  1. Capture the click identifier Google adds to the landing page URL.
  2. Store it on the CRM contact, so it survives the weeks between the click and the outcome.
  3. When the deal closes, send the conversion back to Google with the click ID, the close date, and the deal value.

Now the platform is bidding towards revenue. It can see that one keyword theme produces plenty of leads and no deals, while another produces fewer leads that close, and it can act on that without anyone adjusting a bid by hand.

This pairs with two other pieces of work. The lead qualification gate stops non-buyers being reported as conversions in the first place, and the source repair jobs keep the fields that carry the click identifier from being overwritten by hand.

What it proves

The feedback loop is the whole job.

Most paid media work happens inside the ad platform: bids, budgets, creative, audiences. The larger lever sits outside it, in what you feed back. A platform told about real revenue optimises towards real revenue. A platform told about form fills optimises towards form fills, and no amount of bid tuning fixes that.

Both integrations are unglamorous plumbing that produces no chart anyone wants to look at, and they decide what every other decision is made on.

The Meta work is on my own product and my own spend. The Google work is at LXT, and account identifiers, campaign names, spend, and conversion figures are withheld.