How to configure conversion tracking in Solidres

Conversion tracking tells your analytics and advertising tools that a guest has made a booking, so you can see which campaigns, channels and pages bring bookings and what they are worth. The Conversion tracking plugin sends one conversion for each new booking made on your website, with the booking's value. It never sends anything about the guest.

It works with any tool, in three ways that you can combine:

  • Google Tag Manager and other tag managers read each booking from the data layer, as a standard GA4 purchase.
  • Built-in connectors need only an ID: Google Analytics 4 and Google Ads, Meta Pixel, Microsoft Advertising, TikTok, Matomo, Plausible, Umami and Fathom.
  • Custom code and a browser event, solidres:purchase, reach anything else, from an affiliate network to a customer data platform.

PREREQUISITES:

  • Solidres 4.5.0 or later
  • The Conversion tracking plugin, available to Solidres subscribers
  • A Joomla administrator account that can edit plugins
  • The IDs of the tools you want to use. Each connector below says where its tool shows them.

USING THE GOOGLE ANALYTICS, GOOGLE ADS OR FACEBOOK PLUGIN? Read Upgrading from the Google Analytics, Google Ads and Facebook plugins first. Installing Conversion tracking copies their IDs and switches them off, and the values your tools receive change.

On this page:

  1. What is sent, and when
  2. Getting started
  3. The connectors
  4. IDs for one property or experience
  5. Google Tag Manager and other tag managers
  6. Custom code
  7. For developers: the browser event
  8. Cookie consent
  9. Content Security Policy
  10. Upgrading from the Google Analytics, Google Ads and Facebook plugins
  11. Troubleshooting
  12. Event reference

What is sent, and when

A conversion is sent once for each new booking made on the website, on the page that confirms it:

  • A room booking is sent on the booking confirmation page.
  • An experience booking is sent on the page that follows the booking form. When the guest pays at a payment gateway that takes them off your site, it is sent on the page the gateway brings them back to.

Nothing is sent:

  • when the guest reloads the confirmation page, or opens it again later;
  • when a booking is amended;
  • when the payment failed or was cancelled at the gateway;
  • for bookings entered in the administrator, because they were not made on the website.

Only conversions are sent, never page views. Page view tracking stays with your template, your tag manager or your analytics extension. No guest name, e-mail address, phone number or address is ever sent: a conversion carries the booking code, the amounts, the room types or the experience, the stay's dates and number of guests, and the property's name and location.

Bookings awaiting approval

A property can approve its bookings before they are confirmed (Booking require approval). Such a booking is counted when the guest makes it, flagged as awaiting approval, because the guest is no longer on the website when it is approved. If the property declines many requests, switch Count bookings awaiting approval off; those bookings are then never counted.

Two amounts: revenue and total

Each conversion carries two amounts, and each connector has a Value sent option to choose between them:

Value sent What it is Default for
Revenue before tax The booking's total revenue as the Solidres Revenue report counts it: rooms, extras and fees, less discounts, without tax. Google Analytics 4, Plausible, Umami, Fathom
Total the guest pays The booking total with tax, tourist tax and the payment method's surcharge. Google Ads, Meta, Microsoft Advertising, TikTok, Matomo

Analytics tools get the revenue by default, so that they show the same figure as your Revenue report. Advertising platforms get the total, because their hotel guidance asks for the amount the guest pays. Matomo gets the total as its order's grand total, and the revenue as its subtotal.

For example, three nights at 100 with 10% tax and a tourist tax of 21 give a revenue of 300 and a total of 351. The total is not always the revenue plus the tax: the tourist tax and a payment method's surcharge or discount sit between them.

Getting started

  1. Install the plugin from System → Install → Extensions.
  2. Go to System → Manage → Plugins, search for Conversion tracking and open Solidres - Conversion tracking.
  3. On the tab of each tool you use, enter its IDs (see The connectors). For Google Tag Manager you need no ID here: see Google Tag Manager and other tag managers.
  4. If your site asks visitors for cookie consent, set Consent handling on the Plugin tab (see Cookie consent).
  5. Set Status to Enabled and save.
  6. Make a test booking and check that it arrives (see Troubleshooting).

When the install switches off one of the plugins Conversion tracking replaces, it enables Conversion tracking at the same time, so the bookings those plugins counted are still counted. Otherwise Joomla installs it disabled, and it does nothing until you enable it.

The Plugin tab holds the settings shared by every tool:

  • Publish to the data layer (on by default): pushes each booking to window.dataLayer for tag managers. It sends nothing by itself.
  • Count bookings awaiting approval (on by default): see above.
  • Consent handling, with Consent tool and its categories: see Cookie consent.
The Plugin tab of the Solidres - Conversion tracking plugin: the plugin description, a note on what is sent, Publish to the data layer and Count bookings awaiting approval set to Yes, Consent handling set to Blocked by my consent tool and Consent tool set to CookieConsent v3 / YOOtheme Pro, with Status Enabled on the right

IMPORTANT Send each tool its bookings one way only. If Google Tag Manager already sends purchases to your GA4 property, do not also enter that property's ID on the Google tab; if your template or another extension already sends a Meta purchase, do not also enter the pixel. Otherwise every booking is counted twice.

The connectors

Each connector has its own tab in the plugin's options. Every ID field takes several IDs, one per line, and each ID receives each booking exactly once. An ID in the wrong format is refused when you save.

Every connector also has:

  • Value sent: the revenue before tax or the total the guest pays (see Two amounts).
  • Load the script (on by default): loads the tool's script on the page that sends the booking, without a page view, when the page does not have it yet. Switch it off when your site already loads the tool another way, for example through Google Tag Manager or your template; the booking is then sent to the tool your site loads.
  • Consent category, shown when consent handling is on: the consent the visitor must give before the connector runs.

Google: Google Analytics 4 and Google Ads

Both go through one Google tag.

  • GA4 Measurement IDs: in Google Analytics, Admin → Data streams, then your web stream. They look like G-XXXXXXXXXX. Universal Analytics IDs (UA-) no longer work: Google stopped processing them in July 2023.
  • Google Ads conversions: the conversion ID and label of a conversion action, written together as AW-123456789/AbCdEfGhIjK. Google Ads shows both in the action's event snippet, as send_to. Set the action to use a different value for each conversion, so the booking's value is recorded.
  • Hotel campaign fields (on by default): sends a room booking to Google Ads as a hotel purchase, with the property's Hotel feed ID and the stay's dates, which Hotel campaigns and Performance Max for travel goals read. Experiences are always sent as a plain conversion.
  • Hotel remarketing: marks the booking for hotel_rental dynamic remarketing.
  • Value sent to GA4 (revenue before tax) and Value sent to Google Ads (total the guest pays).
  • Load the Google tag: switch it off when your site already loads the Google tag or Google Tag Manager.

GA4 records the booking as a purchase with its items, the stay's dates and the number of nights, rooms and guests. The plugin never sets Google Consent Mode defaults: your consent tool does, and the Google tag follows them.

The Google tab of the Conversion tracking plugin: a GA4 Measurement ID and a Google Ads conversion in the form AW-123456789/AbCdEfGhIjK, Hotel campaign fields on, Hotel remarketing off, Value sent to GA4 set to Revenue before tax, Value sent to Google Ads set to Total the guest pays, Load the Google tag on and the Consent category

Meta Pixel

  • Pixel IDs: in Meta's Events Manager → Data sources, then your pixel.
  • Hotel campaign fields (on by default): sends a room booking with content_type hotel, the property's Hotel feed ID, the stay's dates, the guests and the property's city, region and country, which Meta's hotel ads read.

Each pixel gets a Purchase with the booking code as its event ID, so you can add the Meta Conversions API later without counting a booking twice. A pixel your template loads and that is not listed here is not sent the booking.

Microsoft Advertising

  • UET tag IDs: in Microsoft Advertising, Tools → UET tag, the tag's ID.
  • Hotel campaign fields (on by default): sends a room booking with the hotel fields (hct_*) that Hotel Price ads and Property Promotion ads read: the total and base price, the stay's dates and length, and the property's Hotel feed ID.

Each tag gets a purchase event. Create a conversion goal of the Event type whose action is purchase.

TikTok

  • Pixel IDs: in TikTok's Events Manager → Web events, your pixel.
  • Hotel campaign fields (on by default): sends a room booking with content_type hotel, the property's Hotel feed ID, the stay's dates, the guests and the property's city, region and country, which Travel Ads read.

Each pixel gets a Purchase with the booking code as its event ID.

Matomo

  • Matomo URL: the address of your Matomo, for example https://example.matomo.cloud/ or https://stats.example.com/matomo/.
  • Site ID: the website's ID in Matomo, under Administration → Websites → Manage.
  • Disable cookies (on by default): tracks the booking without setting Matomo's cookies.

Matomo records an ecommerce order with one line per room type (and one for the extras), and ignores a second order with the same booking code. Ecommerce must be enabled for the website in Matomo. The connector stays out of a page that runs Piwik PRO, which shares Matomo's tracking queue; Piwik PRO's own tag manager reads the data layer instead.

Plausible

  • Script URL: the site script address that Plausible shows in your site's installation instructions, for example https://plausible.io/js/pa-XXXXXXXX.js.
  • Domain: only for the older script that self-hosted Plausible still uses. Give its manual variant (…/js/script.manual.js) as the Script URL and your site's domain here. Leave it empty with the site script.
  • Event name (Booking by default): create a goal for this custom event in Plausible.

Plausible records the booking's revenue against a revenue goal, which needs Plausible's Business plan. Without one, the event is still counted.

Umami

  • Script URL: https://cloud.umami.is/script.js on Umami Cloud, your own server's otherwise.
  • Website ID: under Settings → Websites, your website, the ID in its tracking code.
  • Event name (Booking by default).

Umami records a named event with the booking's revenue and currency, which its revenue reports show.

Fathom

  • Site ID: under Settings → Sites, your site, the Site ID in its embed code.
  • Event name (Booking by default).
  • Add the currency to the event name: for a site that takes several currencies. Each booking is then recorded as Booking EUR, Booking USD and so on.

Fathom takes an event's currency from that event's settings in your Fathom dashboard, not from the booking. Set it there for each event name.

NOTE With Load the script off, Umami and Fathom wait up to ten seconds for your site's own copy of their script, then give up without sending. The other connectors queue the booking until the tool's script arrives.

IDs for one property or experience

The IDs in the plugin's options receive every booking on the site. A property or an experience can add its own, which receive only its bookings. Open the property or the experience and go to its Plugins tab, section Conversion tracking:

The Plugins tab of a property's edit screen, Conversion tracking section: Hotel feed ID, GA4 Measurement ID, Google Ads conversion, Meta Pixel ID, Microsoft UET tag ID and TikTok Pixel ID, each filled with an example ID
  • Hotel feed ID (properties only): the property's ID in your hotel feeds, such as Google Hotel Center, the Meta and TikTok catalogs or Microsoft Property Center. The hotel campaign fields send it so that the platform can match the booking to the hotel. Left empty, the property's Solidres ID is sent.
  • GA4 Measurement ID, Google Ads conversion, Meta Pixel ID, Microsoft UET tag ID and TikTok Pixel ID: one each, in the same formats as in the plugin's options.

A booking goes to the site-wide IDs and to its property's or experience's IDs together; an ID listed in both places still receives it once. Copying a property or an experience copies its IDs, and deleting it deletes them.

On a Solidres Hub site, partners see the same section on their property form and can send their property's bookings to their own accounts. The custom code is never offered to them.

Google Tag Manager and other tag managers

With Publish to the data layer on, each booking is pushed to window.dataLayer as a GA4 ecommerce purchase, the shape Google Tag Manager and most tag managers already understand:

dataLayer.push({ ecommerce: null });
dataLayer.push({
  event: 'purchase',
  ecommerce: {
    transaction_id: 'RS-8F2K1Q',
    value: 300,            // revenue before tax
    tax: 30,
    currency: 'EUR',
    affiliation: 'Hotel Lumen',
    start_date: '2026-11-02',
    end_date: '2026-11-05',
    number_of_nights: 3,
    number_of_rooms: 1,
    adults: 2,
    children: 0,
    items: [{ item_id: 'room-type-7', item_name: 'Deluxe Double', item_category: 'Room',
              item_variant: 'Flexible rate', price: 100, quantity: 3,
              start_date: '2026-11-02', end_date: '2026-11-05' }]
  },
  solidres: { /* the whole booking event: see the Event reference */ }
});

The first push clears the previous ecommerce object, as Google recommends. value is the revenue before tax, as GA4 defines a purchase's value. Anything else, such as the total the guest pays, is in solidres; see the Event reference.

In Google Tag Manager:

  1. Trigger: a Custom Event trigger with the event name purchase.
  2. GA4: a Google Analytics: GA4 Event tag with the event name purchase. Under More Settings → Ecommerce, tick Send Ecommerce data with the Data Layer as its source. Fire it on the trigger above.
  3. Google Ads: a Google Ads Conversion Tracking tag with your conversion ID and label. For its values, create Data Layer Variables named solidres.total (conversion value), ecommerce.transaction_id (transaction ID) and ecommerce.currency (currency code). Fire it on the same trigger.

Other tags read the same variables: ecommerce.value for the revenue, solidres.total for the total, solidres.solidres.property.feed_id for the Hotel feed ID, and so on.

NOTE If Google Tag Manager decides which tags may run, through its consent settings or Google Consent Mode, leave Consent handling at None: the data layer push itself sends nothing, and your tags keep their own consent checks.

Custom code

The Custom code tab takes JavaScript for any other tool: an affiliate network, a metasearch partner's tag, an e-mail platform. It runs once for each booking, on the page that confirms it. Only administrators can see or change it; it is never offered to Hub partners.

The Custom code tab of the Conversion tracking plugin: a Code box holding an affiliate pixel that uses the booking.code, booking.total, booking.currency and property.name tokens, the list of tokens below it, and the Consent category set to Marketing

Write the code without <script> tags. Tokens in double braces are replaced with the booking's values:

Token Prints
{{booking.code}} The booking code, without quotes
{{booking.revenue}}, {{booking.total}}, {{booking.tax}}, {{booking.discount}} A number with a dot as the decimal separator and no thousands separator, for example 351.00
{{booking.currency}} The three-letter currency code, without quotes
{{stay.checkin}}, {{stay.checkout}} A date without quotes: 2026-11-02, or another format such as {{stay.checkin|Ymd}} (20261102). The format uses PHP's date letters.
{{stay.nights}}, {{stay.rooms}}, {{guests.adults}}, {{guests.children}}, {{property.id}} A whole number
{{property.name}}, {{property.feed_id}}, {{booking.coupon}} A complete string with its quotes, safely escaped
{{event_json}}, {{ga4_json}} The whole booking as an object: the booking event, or the GA4 ecommerce object

Numbers, codes and dates print without quotes, so put them in your own quotes where the tool wants a string. Names print as complete quoted strings, so never put them in quotes yourself. For example:

var sale = new Image();
sale.src = 'https://track.example.com/sale'
  + '?order={{booking.code}}'
  + '&amount={{booking.total}}'
  + '&currency={{booking.currency}}'
  + '&hotel=' + encodeURIComponent({{property.name}});

For an experience booking, {{stay.checkin}} and {{stay.checkout}} both print the experience's date, {{guests.adults}} prints its participants and {{property.name}} its name.

The code runs inside a function, so its variables stay local. It does not run when a listener of the solidres:purchase event has taken the booking over (see below). A token the plugin does not know prints nothing, and saving the options warns you about it. The custom code waits for the Consent category on its tab when consent handling is on.

For developers: the browser event

Before anything is sent, the plugin dispatches a solidres:purchase event on window, with the booking as its detail (see the Event reference). It is dispatched by the plugin's deferred script, so a listener must be added by a script that runs earlier, for example in your template's <head>:

window.addEventListener('solidres:purchase', function (event) {
  var booking = event.detail;
  console.log(booking.order_id, booking.revenue, booking.currency);
});

A script that runs later, for example on the page's load event, uses SolidresConversions.subscribe(), which calls it for the bookings already published and for those still to come. This is the simplest way to feed a customer data platform; the booking uses Segment's Order Completed field names, so Segment and RudderStack take it as it is:

window.addEventListener('load', function () {
  var conversions = window.SolidresConversions;
  if (conversions && conversions.subscribe) {
    conversions.subscribe(function (booking, info) {
      if (!info.vetoed) {
        analytics.track('Order Completed', booking);
      }
    });
  }
});

SolidresConversions exists only on a page that publishes a booking.

A solidres:purchase listener that calls event.preventDefault() takes the booking over: the plugin then makes no data layer push, runs no connector and no custom code for it. Use this when another system sends the booking, for example from the server.

Consent handling on the Plugin tab has two settings:

  • None: the connectors and the custom code run as soon as the booking is published. Choose this when your site has no consent tool, or when Google Tag Manager decides which tags may run.
  • Blocked by my consent tool: each connector and the custom code are written into the page blocked, in the markup your consent tool reads, and run once the visitor consents to their category.

The data layer push and the solidres:purchase event are never blocked: they send nothing over the network and set no cookie. The plugin never sets Google Consent Mode defaults or any tool's consent state; your consent tool does.

With Blocked by my consent tool, choose your Consent tool. Each blocked script is written with type="text/plain" and the attribute your tool reads:

Consent tool Attribute Categories
CookieConsent v3 / YOOtheme Pro data-category analytics, marketing
Cookiebot data-cookieconsent statistics, marketing
n3t Cookie Consent data-cookiecategory analytics, marketing
Klaro data-name, with data-type and data-src in place of type and src The names of two Klaro services: analytics and marketing, or the names you enter in Analytics category and Marketing category. Define those services in your Klaro configuration.
Custom attribute The Attribute you enter, for example data-category The values you enter in Analytics category and Marketing category (analytics and marketing when empty)

Choose Custom attribute for a tool that is not listed and blocks scripts by an attribute. Each connector's Consent category says which consent it waits for. By default Meta, Microsoft Advertising, TikTok and the custom code wait for Marketing; Matomo, Plausible, Umami and Fathom for Analytics; Google for Marketing when Google Ads conversions are set, otherwise Analytics.

If the visitor consents while still on the confirmation page, each connector sends the booking then, once. If they leave without consenting, the booking is not sent: the conversion exists only on that page.

Content Security Policy

If your site sends a Content Security Policy, for example through Joomla's System - HTTP Headers plugin, it must allow the tools you use. The plugin's own scripts are files on your site. The custom code is an inline script that carries Joomla's nonce, and each connector loads its tool's script with the same nonce, so a policy with 'strict-dynamic' allows them. Without 'strict-dynamic', add each tool's script host to script-src:

Connector Loads its script from
Googlehttps://www.googletagmanager.com
Meta Pixelhttps://connect.facebook.net
Microsoft Advertisinghttps://bat.bing.com
TikTokhttps://analytics.tiktok.com
MatomoYour Matomo URL
Plausible, UmamiThe host of your Script URL
Fathomhttps://cdn.usefathom.com

Each tool's script then sends to its own servers, which connect-src and img-src must allow. The tools change these hosts from time to time, so follow their own guidance, such as Google's Content Security Policy guide. Your browser's console names every request the policy blocks.

With Blocked by my consent tool, the consent tool re-creates each blocked script when the visitor consents. The connectors are files, so script-src decides whether they may run. The custom code is inline: it runs only if your consent tool keeps the nonce on the script it re-creates. CookieConsent, version 2 (which n3t Cookie Consent is built on) and version 3, keeps it. Klaro does not, so with Klaro a nonce policy can block the custom code; put that code in your tag manager instead. After consenting on a test booking, check the console for a blocked inline script.

Upgrading from the Google Analytics, Google Ads and Facebook plugins

Conversion tracking replaces the Solidres - Google Analytics, Solidres - Google Ads and Solidres - Facebook plugins, which are no longer released. The Google Analytics plugin had recorded nothing since Google stopped processing Universal Analytics in July 2023, Google Ads conversions carried no value, and all three counted a booking again on every reload of its confirmation page.

What the install does

  • It copies their IDs to the Conversion tracking section of each property and experience: a G- Google Analytics ID becomes the GA4 Measurement ID, a Google Ads conversion ID and label become the Google Ads conversion (AW-…/…), and a Facebook pixel ID becomes the Meta Pixel ID. It never overwrites a value already set.
  • It does not copy Universal Analytics IDs (UA-), nor IDs in a format the tools no longer accept. Its message lists each of them with its property or experience, so that you can enter the replacement.
  • On the first install, it switches the three plugins off, so that no booking is counted twice, and enables Conversion tracking. They stay installed, with their settings; uninstall them once you are satisfied.

If you switch one of the old plugins on again, the Conversion tracking options show a warning, because each booking is then counted twice.

What changes

  • The values: Google Analytics now gets the revenue before tax, as the Revenue report counts it, so it shows less than the old plugin sent. Google Ads, Meta and the other advertising platforms get the total the guest pays; Google Ads used to get no value at all. You can change each tool's value in the plugin's options.
  • Counted once: reloads, amendments and failed payments are no longer counted.
  • No page views: the plugin sends conversions only. Track page views with GA4 through your template, Google Tag Manager or an analytics extension.

Troubleshooting

Checking a test booking

Make a booking on your site and, on the confirmation page, open your browser's developer console:

  • dataLayer lists the purchase push, when Publish to the data layer is on.
  • SolidresConversions.subscribe(console.log) prints the booking that was published.
  • The Network tab shows each tool's request. GA4's DebugView, Meta's Test events, TikTok's Test events and Matomo's visitor log show them arrive.

Every test needs a new booking: reloading the confirmation page never sends a booking again.

A booking was not sent

  • Is Solidres - Conversion tracking enabled, with at least one ID, the data layer or custom code set?
  • Was the booking made on the website? Bookings entered in the administrator and amendments are not sent.
  • Did the payment fail, or was it cancelled at the gateway?
  • Does the property approve its bookings, with Count bookings awaiting approval off?
  • With Blocked by my consent tool: did the visitor consent to the connector's category before leaving the confirmation page?
  • Did a browser extension block the tool? Ad blockers stop some visitors' conversions on every website.
  • Bookings paid at a gateway are missing, but others arrive: check that the gateway sends guests back to your site's exact address (with or without www, https). Coming back on another address loses the guest's session, so Solidres shows an empty confirmation and nothing is counted.

A booking was counted twice

  • The same account is reached two ways: Google Tag Manager and the Google connector, or a pixel that your template also sends a purchase to. Use one of them.
  • One of the old Google Analytics, Google Ads or Facebook plugins is enabled again.

The figures differ from the Revenue report

Compare booking by booking, not month by month. A tool counts a booking's revenue on the day it was made; the Revenue report counts it on the nights of the stay. For one booking, the GA4 value equals the booking's total revenue in the report, and the value sent to advertising platforms equals the total on the confirmation page.

Event reference

The booking event is the detail of solidres:purchase, the solidres key of the data layer push and the source of every connector and custom code token. It uses Segment's Order Completed field names, plus a solidres block for what no standard covers. A room booking:

{
  "order_id": "RS-8F2K1Q",
  "affiliation": "Hotel Lumen",
  "revenue": 300,
  "subtotal": 300,
  "tax": 30,
  "total": 351,
  "discount": 0,
  "coupon": "",
  "currency": "EUR",
  "products": [
    { "product_id": "room-type-7", "sku": "room-type-7", "name": "Deluxe Double",
      "category": "Room", "variant": "Flexible rate", "price": 100, "quantity": 3 }
  ],
  "solidres": {
    "schema": 1,
    "kind": "room",
    "awaiting_approval": false,
    "property": { "id": 3, "feed_id": "3", "name": "Hotel Lumen",
                  "city": "Lisbon", "region": "Lisboa", "country": "PT" },
    "stay": { "checkin": "2026-11-02", "checkout": "2026-11-05", "nights": 3,
              "rooms": 1, "adults": 2, "children": 0 }
  }
}
Field Meaning
order_id The booking code. Every tool uses it to ignore a booking it already has. On its own it opens nothing: finding a booking also needs the guest's e-mail address.
affiliation The property's or the experience's name
revenue, subtotal The revenue before tax (see Two amounts)
tax The booking's tax
total The total the guest pays
discount, coupon Every discount the booking was given, and its coupon code (empty when none)
currency The ISO 4217 currency code
products One line per room type, with its room nights as the quantity, the price per night and the rate plan as the variant; plus one extras line when the booking has extras. Fees and booking discounts are not lines.
solidres.schema The version of this shape: 1
solidres.kind room or experience
solidres.awaiting_approval true when the property still has to approve the booking
solidres.property Room bookings: the property's ID, Hotel feed ID, name, city, region and country code
solidres.stay Room bookings: check-in and check-out (YYYY-MM-DD), nights, rooms, adults and children
solidres.experience Experience bookings, instead of property and stay: the experience's ID, name, date and number of participants. Their one product line is experience-{id}, with a quantity of 1.

Amounts are numbers rounded to the currency's decimals.

All the things you need to make your work easier. Did you like Solidres?