Booking Experts data layer overview
Overview, setup checklist, and implementation guidance for the Booking Experts data layer.
This document describes the data layer events emitted by the website and checkout portal, plus how to map them in Google Tag Manager for GA4 and other platforms.
Event reference: See Booking Experts data layer events for the full event list and payloads.
Legacy reference: See Deprecated data layer events for legacy payloads and removals.
Example GTM container: See Example GTM container for a starter configuration.
Pre-release notice: These docs reflect the schema that becomes active on 2 Mar 2026. Events marked active are part of that upcoming schema. Events marked deprecated are legacy payloads still emitted today and will stop after 2 Mar 2026.
Changelog
Legacy: Existing events/payloads currently emitted in production, but superseded by the new schema.
Deprecated: Legacy items scheduled for removal on 2 Mar 2026 (breaking change). They are still emitted today but will stop after that date.
- Breaking changes: Legacy payload variants stop being emitted (same event name, new schema required):
view_item_list,view_item,add_to_cart,remove_from_cart,select_promotion,search,purchase,failed_payment. - Breaking changes: Legacy-only event names stop being emitted:
productImpression,productDetail,promotionClick,propertyDetail,formSubmit,view_price_information,visit_checkout_step,select_amenity,deselect_amenity,ga4purchase,ga4option,ga4optionConfirm,checkout,addToCart,option. - Breaking changes: Legacy remarketing objects stop being emitted:
hrental_*objects andreturn_from_payment. - Breaking changes: Schema enforcement: ecommerce payloads must use
ecommerce.*(ecommerce.items,ecommerce.currency,ecommerce.value, etc.). Top-level legacy keys are no longer emitted. - Breaking changes: Migration required: update GTM triggers and variables to the new event payload structure and replacement events listed in each deprecated section.
Website types and flows
We support two website types: Sales sites (property inquiry forms) and Booking sites (availability search + booking). The checkout portal only exists on Booking sites, and only fires when the guest reaches those steps.
- Sales sites: listings + property detail + inquiry forms; no cart or checkout portal.
- Booking sites: availability search + booking cart + checkout portal (with online payment return).
Sales site flow (example)
view_item_list → select_item → view_item
→ form_start → form_submit → generate_lead
Booking site flow (example)
search_results_loaded → view_item_list → select_item → view_item
→ add_to_cart → view_cart (booking site cart)
→ begin_checkout (checkout portal stay step)
→ checkout_progress (stay step + guest group/license plates updates)
→ view_cart (upgrades step) → checkout_progress (upgrades step)
→ add_to_cart/remove_from_cart (upgrades)
→ add_shipping_info (after details submit)
→ checkout_progress (details step) → checkout_progress (confirm step)
→ add_payment_info (payment portal) → purchase/failed_payment (after payment return)
Booking option flow (example)
generate_lead → close_convert_lead
Site search flow (example)
search → view_search_results
What you need before you start
- A GTM container present on all involved BEX apps/domains (site + checkout + payment return).
- A GA4 property with a web data stream.
- A decision on your Ads/Meta/affiliate stack (and which tags you will run).
Quick start checklist
- Install the GA4 base tag on all pages.
- Enable “Send ecommerce data” in the GA4 tag.
- Create data layer variables for required custom fields.
- Configure cross-domain measurement if multiple domains are involved.
- Configure Consent Mode v2 if using Ads/remarketing.
- Add a Conversion Linker tag if using Google Ads tags.
- Trigger
purchaseand verify in DebugView. - Trigger lead events (
generate_lead/close_convert_lead) if used. - Validate tags with GTM Preview + Tag Assistant.
- Confirm reporting variables are mapped to GA4 custom definitions.
Glossary
- Administration: The PMS administration entity for the booking (used for reporting by park/site/brand).
- Portal settings: Checkout portal configuration snapshot included with events to analyze form requirements.
- Preferences: Guest preferences/amenities selected during the checkout flow.
- Special period: Seasonal or promotional period used in availability/search reporting.
- Customer type:
neworreturning, derived from booking history in the last 540 days.
Objects vs GA4 parameters
- GA4 has strict naming/length limits for event/parameter names and values.
- BEX emits rich objects like
bookingandportal_settingsfor GTM variable extraction. - Do not send entire objects to GA4 as a parameter; extract only the scalar fields you need for reporting.
Event availability matrix
Definitions: Sales/Booking site refer to CMS website pages; Checkout portal refers to the separate checkout portal app/domain used after cart. Flow describes where the event occurs in the journey.
| Event | Status | Sales site | Booking site | Checkout portal |
|---|---|---|---|---|
add_to_cart | Active | No | Yes | Yes |
remove_from_cart | Active | No | Yes | Yes |
view_cart | Active | No | Yes | Yes |
begin_checkout | Active | No | No | Yes |
add_shipping_info | Active | No | No | Yes |
add_payment_info | Active | No | No | Yes |
purchase | Active | No | No | Yes |
generate_lead | Active | Yes | No | Yes |
close_convert_lead | Active | No | No | Yes |
view_item_list | Active | Yes | Yes | No |
select_item | Active | Yes | Yes | No |
view_item | Active | Yes | Yes | No |
view_promotion | Active | No | Yes | Yes |
select_promotion | Active | No | Yes | Yes |
view_search_results | Active | Yes | Yes | No |
search | Active | Yes | Yes | No |
search_results_loaded | Active | No | Yes | No |
checkout_progress | Active | No | No | Yes |
form_start | Active | Yes | Yes | No |
form_submit | Active | Yes | Yes | No |
failed_payment | Active | No | No | Yes |
user_identity_update | Active | No | No | Yes |
view_item_list | Deprecated (removal 2 Mar 2026) | No | Yes | No |
view_item | Deprecated (removal 2 Mar 2026) | No | Yes | No |
add_to_cart | Deprecated (removal 2 Mar 2026) | No | Yes | No |
remove_from_cart | Deprecated (removal 2 Mar 2026) | No | Yes | No |
select_promotion | Deprecated (removal 2 Mar 2026) | Yes | Yes | No |
search | Deprecated (removal 2 Mar 2026) | Yes | Yes | No |
productImpression | Deprecated (removal 2 Mar 2026) | No | Yes | No |
productDetail | Deprecated (removal 2 Mar 2026) | No | Yes | No |
promotionClick | Deprecated (removal 2 Mar 2026) | Yes | Yes | No |
propertyDetail | Deprecated (removal 2 Mar 2026) | Yes | No | No |
formSubmit | Deprecated (removal 2 Mar 2026) | Yes | No | No |
view_price_information | Deprecated (removal 2 Mar 2026) | No | Yes | Yes |
visit_checkout_step | Deprecated (removal 2 Mar 2026) | No | Yes | Yes |
select_amenity | Deprecated (removal 2 Mar 2026) | No | Yes | Yes |
deselect_amenity | Deprecated (removal 2 Mar 2026) | No | Yes | Yes |
ga4purchase | Deprecated (removal 2 Mar 2026) | No | Yes | No |
ga4option | Deprecated (removal 2 Mar 2026) | No | Yes | Yes |
ga4optionConfirm | Deprecated (removal 2 Mar 2026) | No | Yes | Yes |
purchase | Deprecated (removal 2 Mar 2026) | No | Yes | No |
checkout | Deprecated (removal 2 Mar 2026) | No | Yes | Yes |
addToCart | Deprecated (removal 2 Mar 2026) | No | Yes | Yes |
option | Deprecated (removal 2 Mar 2026) | No | Yes | Yes |
failed_payment | Deprecated (removal 2 Mar 2026) | No | Yes | No |
hrental_home | Deprecated (removal 2 Mar 2026) | No | Yes | No |
hrental_home_subsite | Deprecated (removal 2 Mar 2026) | No | Yes | No |
hrental_searchresults | Deprecated (removal 2 Mar 2026) | No | Yes | No |
hrental_offerdetail | Deprecated (removal 2 Mar 2026) | No | Yes | No |
hrental_conversion | Deprecated (removal 2 Mar 2026) | No | Yes | No |
return_from_payment | Deprecated (removal 2 Mar 2026) | No | Yes | No |
Core GTM configuration
- Create a GA4 configuration tag (Google tag / GA4 config) that fires on all pages to initialize the data stream.
- Create a Google Analytics: GA4 Event tag with Event Name
{{Event}}. - In More Settings → Ecommerce, enable Send Ecommerce data with Data Source Data Layer.
- Create data layer variables for every
customparameter you want to report on. - Trigger the GA4 Event tag with Custom Event triggers matching the data layer event name (for example
purchase), not ongtm.jsorgtm.load. - Publish your GTM container and validate in GA4 realtime/debug view.
Parameter types: ga4 fields are standard GA4 parameters. custom fields need data layer variables in GTM and custom definitions in GA4 to be reportable.
Copy/paste safety: do not quote numeric values for value, price, quantity, or index.
Reference: GA4 ecommerce via GTM
GA4 enhanced measurement (avoid duplicates)
If you rely on BEX events, disable overlapping GA4 Enhanced Measurement features in the data stream settings to avoid double counting.
- Form interactions: disable if you use
form_startandform_submitfrom BEX. - Site search: disable if you use
view_search_results(andsearchif mapped) from BEX.
GA4 reporting
What to register in GA4
Register custom parameters as custom dimensions or metrics in GA4 (Admin → Custom definitions) only if you need them in reports or audiences.
Recommended GA4 reporting dimensions for BEX
GA4 parameter names may contain only letters, numbers, and underscores, and must start with a letter. Keep dotted data layer paths in GTM, but map them to underscore parameter names in GA4.
Register custom definitions with the correct scope. Item fields live inside ecommerce.items[] and require item-scoped custom dimensions.
BEX provides user.user_id in the data layer. If you want GA4 User-ID, map that value to the GA4 tag User-ID field (built-in user_id, not a user property or custom definition).
If you prefer user properties, use GA4-safe names like bex_user_id and bex_returning_customer. Treat these identifiers as pseudonymous and only set them when your privacy policy and consent allow it.
| Data layer path | GA4 parameter name | Scope | GTM mapping |
|---|---|---|---|
ecommerce.customer_type | customer_type | EVENT | GA4 Event tag -> Event Parameters |
search.special_period_id | search_special_period_id | EVENT | GA4 Event tag -> Event Parameters |
search.special_period_name | search_special_period_name | EVENT | GA4 Event tag -> Event Parameters |
search.amenities_count | search_amenities_count | EVENT | GA4 Event tag -> Event Parameters |
search.nights | search_nights | EVENT | GA4 Event tag -> Event Parameters |
portal_settings.required_fields_count | portal_settings_required_fields_count | EVENT | GA4 Event tag -> Event Parameters |
portal_settings.optional_fields_count | portal_settings_optional_fields_count | EVENT | GA4 Event tag -> Event Parameters |
booking.status | booking_status | EVENT | GA4 Event tag -> Event Parameters |
ecommerce.items[].administration_id | administration_id | ITEM | Items array (item-scoped custom dimension) |
ecommerce.items[].administration_name | administration_name | ITEM | Items array (item-scoped custom dimension) |
ecommerce.items[].item_country | item_country | ITEM | Items array (item-scoped custom dimension) |
ecommerce.items[].item_region | item_region | ITEM | Items array (item-scoped custom dimension) |
ecommerce.items[].item_city | item_city | ITEM | Items array (item-scoped custom dimension) |
ecommerce.items[].item_variant | item_variant | ITEM | Items array (built-in GA4 item parameter; no custom definition) |
user.user_id | user_id (built-in) | USER-ID | GA4 tag User-ID field |
user.user_id | bex_user_id | USER | GA4 Event tag -> User Properties |
user.returning_customer | bex_returning_customer | USER | GA4 Event tag -> User Properties |
PII and user_data (important)
Do NOT register or report the following in GA4:
user_data.sha256_email_addressuser_data.sha256_phone_number
Never send email or phone numbers to GA4, even when hashed. GA4 does not allow PII collection.
user_data is hashed identifiers for marketing platforms (for example Google Ads Enhanced Conversions and Meta Advanced Matching). It must be sent only when marketing consent is granted and must not be mapped into GA4 tags.
Google Ads conversions
BEX emits GA4-recommended ecommerce events (e.g. purchase, begin_checkout, add_to_cart). In Google Ads you typically choose between:
- Option A — Import GA4 key events into Google Ads: simpler setup if GA4 is your source of truth.
- Option B — Implement Google Ads tags via GTM (recommended): Google Ads-native conversion measurement and advanced features (Enhanced Conversions, remarketing).
Minimum Google Ads tag set in GTM (Option B)
1) Conversion Linker (required)
Create a Conversion Linker tag and trigger it on All Pages.
If your flow spans multiple domains:
- Enable Link across domains.
- Fill Auto Link Domains with all involved domains.
- Optional: enable Decorate Forms if form submits navigate across domains.
2) Google Ads Conversion Tracking (purchase)
- Trigger: Custom Event =
purchase - Conversion ID + Conversion Label from Google Ads
- Conversion Value:
{{DLV - ecommerce.value}} - Currency Code:
{{DLV - ecommerce.currency}} - Transaction ID:
{{DLV - ecommerce.transaction_id}}(recommended)
BEX mapping: ecommerce.transaction_id → Order ID, ecommerce.value → conversion value, ecommerce.currency → currency.
3) Optional: secondary conversions
generate_lead(lead submitted)close_convert_lead(booking option converted)
Mark these as secondary in Google Ads unless you want them as a main bidding signal.
Dynamic remarketing (optional, recommended if you use feeds)
If you run dynamic remarketing, maintain a matching feed and send event data with items[] so Google Ads can match items[].id to the feed Property ID.
For BEX, map:
items[].id=ecommerce.items[].item_iditems[].google_business_vertical=hotel_rentalitems[].start_date=ecommerce.items[].start_dateitems[].end_date=ecommerce.items[].end_date
Recommended event names: view_item, view_item_list, view_search_results, add_to_cart, purchase.
Enhanced Conversions (recommended; requires Consent Mode v2)
Enhanced Conversions uses consented user-provided data to improve conversion matching.
Consent requirements
Only send user-provided data when ad_user_data = granted (and marketing consent is granted).
BEX data available
user_data.sha256_email_addressuser_data.sha256_phone_number
GTM implementation approach (recommended with BEX)
- Create a data layer variable named
user_data(data layer variable name:user_data). - Create a User-Provided Data variable in GTM using Code configuration from the
user_dataobject. - Assign that user-provided data variable to Google tags via the
user_dataparameter or use the Google Ads User-Provided Data Event tag.
Triggering
Fire user-provided data collection on the same events where BEX provides user_data:
purchasegenerate_lead(optional)
Implementation diagram
- BEX user action triggers
purchase. dataLayer.push({ event: "purchase", ecommerce: {...}, user_data: {...} })- GTM Trigger: Custom Event
purchase. - Conversion Linker runs on All Pages (with cross-domain linking enabled).
- Google Ads Conversion Tracking tag fires for
purchase. - Google Ads conversion is recorded.
How to configure in GTM (short)
- Tags → New → Conversion Linker → Trigger: All Pages (enable link across domains + Auto Link Domains).
- Tags → New → Google Ads Conversion Tracking → ID/Label from Google Ads → map
ecommerce.*→ Trigger:purchase. - Optional: Remarketing tag with dynamic remarketing parameters and
items[]. - Optional: Enhanced Conversions using User-Provided Data variable (only when
ad_user_data = granted).
Meta Ads (Pixel / Conversions API)
Event mapping (BEX → Meta)
| BEX event | Meta event |
|---|---|
view_item | ViewContent |
view_item_list | trackCustom (ViewItemList) |
select_item | trackCustom (SelectItem) |
view_search_results | Search |
add_to_cart | AddToCart |
begin_checkout | InitiateCheckout |
add_payment_info | AddPaymentInfo |
purchase | Purchase |
generate_lead | Lead |
Map ViewContent only for view_item (product detail views). Do not map list or click events to ViewContent.
Fire Meta Search only once per user search. Use view_search_results as the canonical event and do not map search to avoid double counting.
Required parameters
value→ecommerce.valuecurrency→ecommerce.currencycontent_ids→ derived fromecommerce.items[].item_idcontent_type→product
Search events: set Meta search_string from the BEX/GA4 search_term value (from search or view_search_results).
Event deduplication (Pixel + CAPI)
- Pixel uses
eventIDand CAPI usesevent_id; the values must match for deduplication. - Recommended defaults:
event_id = ecommerce.transaction_idfor purchases,event_id = lead_idfor leads (checkout portal uses the booking number). Use the same value for PixeleventID.
Advanced matching
When marketing consent is granted:
- Pass
user_data.sha256_email_address - Pass
user_data.sha256_phone_number
Notes: only send with marketing consent; never send raw email or phone values; do not reuse these fields for GA4 reporting.
TradeTracker
TradeTracker conversion tracking should fire only on the final conversion event: purchase. Do not fire on pageviews or intermediate checkout steps.
Required TradeTracker configuration (GTM template)
- Campaign ID (static; provided by TradeTracker)
- ProductGroup ID (static; provided by TradeTracker)
- Transaction ID (dynamic; from BEX)
- Transaction Amount (dynamic; from BEX)
- Optional fields depending on program configuration (for example voucher)
BEX mapping (Sales tag)
- Transaction ID →
ecommerce.transaction_id - Transaction Amount →
booking.base_rent_total(ensure this matches your TradeTracker commission agreement) - Currency →
ecommerce.currency - Voucher code →
ecommerce.coupon(if used)
Rule: TradeTracker amount must match the affiliate agreement definition (base rent vs gross booking value). Default: booking.base_rent_total.
Implementation diagram
- BEX emits
purchase. dataLayer.push({ event: "purchase", ecommerce: { transaction_id, value, currency, ... }, booking: { base_rent_total } })- GTM Trigger: Custom Event
purchase. - TradeTracker Sales Tag fires with Campaign ID, ProductGroup ID, and transaction variables.
- TradeTracker conversion is recorded.
How to configure in GTM (short)
- Import the TradeTracker template.
- Create constants:
TT - Campaign ID,TT - ProductGroup ID. - Create data layer variables:
DLV - ecommerce.transaction_id,DLV - booking.base_rent_total,DLV - ecommerce.currency. - Create the Sales tag with the above variables and trigger on
purchase. - Test in GTM Preview and validate the conversion once per
transaction_id.
Cross-domain tracking & Consent
BEX flows often span multiple domains. Configure both GA4 cross-domain measurement and Conversion Linker cross-domain linking.
Cross-domain measurement (GA4)
- Use one GA4 property + one Web data stream.
- Use the same tag ID across all domains.
- Admin → Data Streams → select your Web stream → Configure tag settings → Configure your domains.
- Add all involved domains and verify the
_gllinker parameter on navigation. - If redirects drop query parameters, cross-domain will fail; preserve
_gl. - Reference: GA4 cross-domain measurement
Google Ads cross-domain (Conversion Linker)
- Add a Conversion Linker (All Pages).
- Enable link across domains and set Auto Link Domains.
- Optional: Decorate Forms if you submit forms across domains.
Consent Mode v2 (Google tags)
- Consent types:
analytics_storage,ad_storage,ad_user_data,ad_personalization. - When
analytics_storage = denied, GA4 can send cookieless pings in Advanced Consent Mode; Basic mode blocks tags until consent. - When
ad_user_data = denied, user-provided-data use cases (Enhanced Conversions) are disabled.
Recommended consent enforcement model
Definitions: Marketing consent means analytics + ads consent (grant analytics_storage, ad_storage, ad_user_data, ad_personalization). Analytics only means grant analytics_storage while ads consent stays denied.
Recommended mode: Advanced Consent Mode v2. In GTM, set default consent to denied in a Consent Initialization tag, enable consent checks on GA4/Ads tags, and update consent when the user makes a choice. Use Basic mode only if you must block all tags until consent.
| User choice | GA4 (analytics) | Google Ads conversion tags | user_data |
|---|---|---|---|
| Marketing consent | Allowed | Allowed | Allowed |
| Analytics only | Allowed | Blocked | Blocked |
| No consent | Cookieless (Advanced) / Blocked (Basic) | Cookieless/limited (Advanced) / Blocked (Basic) | Blocked |
Implementation diagram
| Domain A (site) | Domain B (checkout/payment) |
|---|---|
| Page load | Navigation to domain B |
| Consent defaults set early | GA4 cross-domain preserves IDs via _gl |
| GTM tags evaluate consent | Conversion Linker preserves click IDs |
| GA4 / Ads tags fire with consent signals | purchase event attributed correctly |
How to configure in GTM (short)
- Consent defaults: trigger Consent Initialization on All Pages.
- Consent update: run consent update before marketing tags fire.
- Cross-domain: configure GA4 domains list + Conversion Linker link across domains.