I have a confession that every hotelier reading this has probably lived: for the first stretch of running campaigns, I genuinely could not tell you how many bookings my marketing produced. The booking engine said one number. Google Ads said another. GA4 said a third, and it was always the lowest and always the wrongest. I was making budget decisions on vibes.
The culprit, nine times out of ten, was the handoff between the website and the booking engine. That seam is where revenue data goes to die. So this is the post I wish someone had handed me: exactly how I wire a hotel booking engine into GA4 so the purchase event fires, the revenue lands, and the number in GA4 actually resembles reality.
This is not a general GA4 setup post. If you have not done the basics, go read my hotel SEO 2026 starter guide and get your property properties straight first. This one is laser-focused on the booking-engine-to-GA4 plumbing.
Why this seam breaks revenue tracking
Here is the thing that took me embarrassingly long to internalize. Most independent hotels run their marketing site on one domain and their booking engine on a completely different one. You are on yourhotel.com, and the moment a guest clicks “Book Now,” they get flung over to something like secure.bookingvendor.com or reservations.yourhotel.com.
To GA4, those are two different houses. By default, when a visitor walks from one house to the other, GA4 treats it as the same person leaving and a brand new stranger arriving. The original session ends. A new session starts. And when the booking completes on the vendor domain, GA4 either attributes that purchase to “direct / none” or to a brand new session that has no memory of the Google search or the paid ad that actually drove it.
So your beautifully optimized organic traffic gets zero credit, and you start wondering why SEO “doesn’t convert.” It converts fine. The tracking just lost the thread.
The single most common reason a hotel underreports direct bookings in GA4 is unconfigured cross-domain tracking between the marketing site and the booking engine. Fix that one thing and you often recover the majority of your “missing” conversions overnight.
There is a second, sneakier problem: even when the session survives, the purchase event frequently never fires with real ecommerce parameters. The booking engine throws up a “thank you” page, but it does not tell GA4 what was bought or for how much. So you get a conversion count with no revenue attached, which is almost as useless as no data at all.
We fix both. In order.
Step 1: Audit what you actually have
Before touching anything, I figure out which of three worlds I am in:
- The booking engine lives on the same domain (a subfolder like
yourhotel.com/reservations). Lucky you. Cross-domain is a non-issue. Skip to the ecommerce event. - The booking engine lives on a subdomain (
book.yourhotel.com). GA4 handles same-root subdomains automatically if both pages share the same GA4 tag and measurement ID. Usually fine, but verify. - The booking engine lives on a totally different domain (
bookvendor.io/yourhotel). This is the one that needs real cross-domain configuration.
The fastest audit: open your site, open the browser dev tools network tab, click “Book Now,” and watch the URL. Does the GA4 client ID (_ga parameter) get appended to the destination URL when you land on the booking engine? If yes, linking is working. If the URL is naked, it is not.
I also check whether the booking vendor even allows you to add tracking. Some locked-down engines do not. If yours is a black box that refuses any GA4 or GTM injection, that is a strategic problem worth raising, and one I dig into in metasearch for independent hotels because your tech stack quietly shapes how much direct business you can capture.
Step 2: Configure cross-domain tracking
In GA4, this lives under Admin, then Data Streams, then your web stream, then Configure tag settings, then Configure your domains. You list every domain involved in a single guest journey:
yourhotel.combookvendor.io(or whatever your engine domain is)
GA4 then automatically decorates outbound links to those domains with the linker parameter, so the client ID travels across. The catch: the same GA4 measurement tag has to be present on the booking engine pages too. If you can only put the tag on your marketing site and the vendor refuses to load it on their pages, cross-domain linking cannot complete, because there is no tag on the other side to read the passed client ID.
This is why I push so hard on vendor selection. A booking engine that lets you drop in GTM is worth real money in measurement clarity alone. When I evaluate a client’s stack as part of our book-direct CRO work, the first question is always: can I get a tag onto the booking pages? If not, the honest answer is that revenue tracking will be approximate forever.
After configuring, I test by clicking through and confirming the _ga linker parameter appears on the destination URL. Realtime reports in GA4 should now show a single continuous session walking from site to engine.
Step 3: Send the purchase event with real ecommerce data
This is the part that actually captures revenue. GA4 has a standard purchase event with a defined ecommerce schema, and the goal is to fire it on the booking confirmation page with the right parameters.
A clean GA4 purchase event for a hotel looks like this in concept:
| Parameter | What I send | Example |
|---|---|---|
transaction_id | The confirmation number | ABC123456 |
value | Room revenue (I exclude tax and fees) | 540.00 |
currency | ISO code | USD |
items | The room or rate booked | Deluxe King, 2 nights |
coupon | Promo code if used | DIRECT10 |
The transaction_id matters more than people think. GA4 uses it to de-duplicate, so if a guest refreshes the confirmation page three times, you do not get three phantom bookings. Always pass the real confirmation number.
If your booking engine supports a data layer, the implementation is clean: the engine pushes booking details into dataLayer on the confirmation page, and a GTM tag reads those values and fires the GA4 purchase event. If the engine does not expose a data layer but does show the values in the page or URL, you can sometimes scrape them with GTM variables, though that is more fragile and I treat it as a stopgap.
The thing I refuse to do is hardcode a flat value. I have seen setups that send value: 1 on every booking just to make a conversion register. That gives you a count but destroys your revenue reporting and your return-on-ad-spend math. Send real money or do not bother.
Step 4: Decide your revenue definition and stick to it
Here is a question that sounds trivial and is not: what number is “the booking worth”?
- Room revenue only?
- Room plus taxes?
- Room plus taxes plus resort fees plus add-ons?
There is no universally correct answer, but there is a wrong behavior, which is being inconsistent. I almost always send room revenue excluding taxes and fees, because that is the number I make marketing decisions against and it reconciles most cleanly with what the property thinks of as “revenue.” Whatever you choose, write it down in a one-line spec so the next person, or the next agency, does not silently change it and break your trend lines.
The goal is not perfect accounting. The goal is a GA4 revenue number that moves the same direction and roughly the same magnitude as your booking engine, so you can trust it enough to make decisions. Directional truth beats false precision.
Step 5: Reconcile against the booking engine and PMS
Tracking you have not reconciled is tracking you cannot trust. So once the purchase event is live, I let it run a couple of weeks and then sit down with three numbers for the same date range:
- GA4 reported purchases and revenue
- Booking engine reported reservations and revenue
- PMS confirmed bookings, net of cancellations
They will never match exactly, and that is okay. GA4 will usually run a little under the booking engine because of consent opt-outs, ad blockers, and the occasional broken session. What I care about is whether the gap is stable and explainable. A consistent 10 to 15 percent under-count from privacy tooling is normal and fine. A 60 percent gap means cross-domain is still broken or the purchase event is misfiring, and I go back to Step 2.
Reconcile GA4 against the booking engine and the PMS for the same window before you trust a single report. A stable, explainable gap is healthy. A wild gap means the plumbing is leaking and your conversion data is fiction.
Why this matters for the bigger fight
You might be wondering why an SEO agency obsesses over GA4 plumbing. Simple: the entire case for investing in direct-booking marketing rests on being able to prove it works. The OTAs charge roughly 15 to 25 percent commission on every reservation they send you, and the whole point of our hotel SEO program and direct-booking CRO is to win back a healthier share of bookings directly so you are less dependent on that channel.
But if your GA4 cannot see direct bookings, you can never show the owner that organic search produced revenue, which means the direct-booking budget gets cut, which means you lean harder on the OTAs. Clean revenue tracking is what keeps the whole flywheel honest. I lay out the commission math in detail over in the book-direct math post if you want the numbers.
To be clear about what I am and am not promising: wiring up GA4 does not, by itself, get you a single extra booking, and nobody honest can guarantee you a ranking or a revenue outcome. What it does is give you the measurement to make every other decision smarter, and to reduce OTA dependence with evidence instead of hope.
A few traps I have learned to check for
- Duplicate purchase events. If both the booking engine’s native GA4 integration and your GTM tag fire a purchase, you double-count revenue. Pick one source of truth for the purchase event and kill the other.
- Consent mode breaking the session. If your cookie banner blocks GA4 until consent and the guest books before clicking accept, the purchase may never record. Make sure your consent setup and GA4 actually cooperate.
- The confirmation page that redirects too fast. Some engines bounce the guest off the thank-you page before the tag fires. If your event count is suspiciously low, check whether the page lingers long enough to send.
- Self-referral pollution. Even with cross-domain configured, list the booking engine domain in your unwanted referrals so GA4 does not credit the vendor domain as the traffic source.
Where to start tomorrow
If you do nothing else this week, do the dev-tools test from Step 1: click “Book Now” and watch whether the _ga parameter rides along to the booking engine. That single check tells you whether you are bleeding attribution at the seam. From there, configure cross-domain, get a real purchase event firing with actual revenue, and reconcile against your PMS.
It is fiddly, vendor-dependent work, and honestly it is the kind of thing that is easy to get 80 percent right and quietly wrong on the last 20 percent that matters. If you would rather not spend three weekends in GTM and want someone to wire it up so your revenue data is trustworthy from day one, that is exactly the kind of unglamorous, decision-critical work we handle inside our book-direct CRO service. Book a call over on the contact page and we will pressure-test your tracking before you spend another dollar on campaigns you cannot measure.