Google’s uploadClickConversions API rejects any conversion without a gclid, wbraid, or gbraid. The booking-tracker records bookings after the fact — the guest is long gone, they called or messaged on LINE, and there is no click ID to attach. So the most obvious path for feeding real bookings back into Google Ads was closed before it started.
This piece and “Real Bookings Replace Click Proxies in the Ads Conversion Pipeline” cover the same system from different angles. That piece covers why real bookings replaced on-site CTA click proxies as the optimization target. This one covers a narrower, harder problem underneath it: when one operator runs two properties on two separate Google Ads accounts, how does a single shared booking form route each confirmed booking to the correct account — without any click identifier to attribute it?
Why this is a routing problem, not a tracking problem
The larger system here is the direct-booking channel for a B&B operator who keeps bookings in their head and on paper. The ads engine optimizes spend against real confirmed bookings instead of click proxies. For that to work, a booking logged in the tracker has to become a conversion signal inside Google Ads — valued correctly, attributed to the right campaign.
The complication: this operator runs two properties, and each property advertises on its own Google Ads account. A booking for property A must reach account A; a booking for property B must reach account B. The booking form is shared. So the form’s POST handler has to know, per booking, where the conversion belongs.
Three options were on the table, and the decision record walks through all three.
Why not the Conversion API directly (Route B)
The instinct is to skip GA4 entirely and call the Google Ads Conversion API server-side from the booking POST. Cleaner attribution, no analytics middle layer.
It doesn’t work here. uploadClickConversions requires gclid / wbraid / gbraid. These bookings have none — the guest left the site days ago and contacted the owner off-platform. Send a conversion with an empty click ID and the API returns 400 Invalid. There is no “unattributed conversion” mode in that endpoint. Route B was dead on arrival for this booking model.
Why not two separate GA4 properties (Route A)
The textbook answer for “two ad accounts” is two GA4 properties — one per account, each with its own data stream, each linked to its own Ads account. Clean separation, no routing logic in code.
The cost is operational, not technical. It means standing up two new GA4 properties, reconfiguring data streams, and re-linking Ads accounts that already had working attribution on the existing property. The risk of breaking the on-site CTA click tracking that was already feeding Smart Bidding was real, and the payoff was a separation I could get another way.
Why event-name routing (Route C)
Google Ads lets you import a specific GA4 event as a conversion, scoped to a specific Ads account. That’s the lever. One shared GA4 property, one Measurement Protocol endpoint — but each property in the booking tracker carries its own ads_event_name. Property A’s bookings fire booking_confirmed_a; property B’s fire booking_confirmed_b. Account A imports only the first event as a conversion; account B imports only the second. The routing happens in Google Ads’ import configuration, not in fragile per-account credential juggling in my code.
The implementation is small precisely because the design is right. The POST handler reads the property’s ads_event_name and sends it through the Measurement Protocol with the booking’s value. The credential moved from per-operator GA4 secrets in the database to a single environment-level Measurement ID and API secret — there’s exactly one GA4 property now, so storing per-operator GA4 config made no sense.
Valuing the conversion: nights, not bookings
A conversion signal is only as good as the value attached to it. A flat “a booking happened” tells Smart Bidding nothing about whether this was a one-night single room or a seven-night whole-house rental. So the value is computed as nights × per-night value, with a different per-night figure for whole-house versus single-room bookings. A long whole-house booking now outweighs a short single-room one in the bidding model — which is exactly the revenue reality the operator lives in.
This matters more than it looks. Without dynamic value, Smart Bidding optimizes toward booking count, and a campaign that drives lots of cheap one-night singles looks better than one driving a few high-value long stays. Indexing the conversion value to nights aligns the bidding objective with actual revenue.
The honest caveat
All of this assumes bookings actually get logged. The proxy metric — on-site CTA button clicks — existed for a real reason: the operator records bookings on paper, and there was no low-friction digital path. The whole point of the booking tracker is to be the low-friction path. But its success depends on the discipline of daily logging holding up. The routing is correct; whether the data flows depends on a human habit that isn’t guaranteed yet.
This is the kind of work that didn’t used to happen for a two-property B&B — not because nobody wanted it, but because the engineering, analytics, and ad-ops to wire it up cost more than a small operator could justify. The fact that it exists at all is the part worth noticing.
Google gives you an attribution model; the booking model you actually have decides which one you’re allowed to use.