Skip to main content

How to track conversions from your embedded booking widget

Set up cross-domain conversion tracking between your website's embedded booking widget and your Understory storefront using Google Tag Manager and GA4.

Updated this week

When guests book through the embedded widget on your website (e.g., yourdomain.com), they pass through a payment gateway and land on your Understory storefront (e.g., yourcompany.understory.io) for the receipt. Because these are different domains, Google Analytics treats them as separate visits — meaning you can't attribute completed bookings back to your marketing efforts.

Understory solves this by automatically bridging the analytics session between your website and your storefront. Depending on your tracking setup, this may work automatically or require a small one-time configuration in Google Tag Manager.


What Understory does automatically

When a guest starts the booking flow from your embedded widget, Understory captures their Google Analytics session data (client ID and session ID) from your website. This data is carried through the payment process and made available on your storefront via the dataLayer.

Specifically, Understory pushes a session_bridge event to the dataLayer with two values:

  • ga_client_id — the GA4 client identifier from your website

  • ga_session_id — the GA4 session identifier from your website

You don't need to do anything to enable this — it happens automatically.


Setup depends on your tracking configuration

Option A: You use Understory's built-in GA4 integration

If you added your GA4 Measurement ID in Growth > Tracking and do not use your own GTM container, session bridging works automatically. Understory's GTM container reads the bridged session data and configures GA4 with the correct client ID and session ID.

You still need to configure cross-domain measurement in GA4 (Step 1 below) so that GA4 recognizes your website and storefront as related domains.

Option B: You use your own GTM container

If you enabled your own GTM container (see Using Your Own Google Tag Manager Container), you need to configure your GA4 tag to read the bridged session data. Follow all steps below.

Option C: You imported the Understory GTM template

If you imported the Understory GTM storefront template after March 2026, the session bridge variables are already included. Just make sure you've configured cross-domain measurement in GA4 (Step 1 below).


How to set up cross-domain conversion tracking

Step 1: Configure cross-domain measurement in GA4

Required for all setups. This tells Google Analytics that your website and your Understory storefront belong together.

  1. Open Google Analytics — go to Admin at the bottom of the left menu

  2. Select Data Streams — then click on your web stream

  3. Click "Configure tag settings" — then select Cross-domain measurement

  4. Add your storefront domain — enter yourcompany.understory.io (replace with your actual storefront domain)

  5. Save — cross-domain measurement is now active

If you use Understory's built-in GA4 integration (Option A), you're done. The remaining steps are only for customers using their own GTM container.


Step 2: Create Data Layer Variables in GTM

These variables let your GA4 tag read the session data that Understory passes on the receipt page.

  1. Open Google Tag Manager — go to Variables in the left menu

  2. Click New — under "User-Defined Variables"

  3. Choose "Data Layer Variable" as the variable type

  4. Set the variable name to DLV - ga_client_id — and set Data Layer Variable Name to ga_client_id

  5. Save — then repeat the process for a second variable

  6. Create another Data Layer Variable — name it DLV - ga_session_id with Data Layer Variable Name set to ga_session_id

  7. Save — you now have both variables ready


Step 3: Create fallback variables for safe field overrides

You need variables that return the bridged value when available, and fall back to the automatic GA4 value when it's not. This prevents duplicate tracking.

  1. Go to Variables — click New under "User-Defined Variables"

  2. Choose "Custom JavaScript" as the variable type

  3. Name it CJS - Client ID Override — and enter this code:

function() {
  var val = ##{{DLV - ga_client_id}};
  return val ? val : undefined;
}

  1. Save — then create a second Custom JavaScript variable

  2. Name it CJS - Session ID Override — and enter this code:

function() {
  var val = ##{{DLV - ga_session_id}};
  return val ? val : undefined;
}

  1. Save — these variables return undefined when no bridged data is available, which tells GA4 to use its default values


Step 4: Add session bridging to your existing Google Tag

Important: Do not create a separate Google Tag for this. Modify your existing Google Tag to avoid duplicate events.

  1. Open your existing Google Tag — the one that fires on All Pages with your GA4 Measurement ID

  2. Expand "Configuration settings" — then click Fields to set

  3. Add two fields:

    • Field Name: client_id — Value: ##{{CJS - Client ID Override}}

    • Field Name: session_id — Value: ##{{CJS - Session ID Override}}

  4. Save the tag

When a guest arrives from your widget, these fields use the bridged values from your website. For all other visits, the values are undefined and GA4 uses its default behavior. This means your purchase event fires exactly once with the correct session — no duplicates.

Technical background: Setting client_id and session_id via Fields to Set is the documented GA4 approach for manual cross-domain measurement. It's the same mechanism as the automatic _gl linker parameter, but works reliably across payment gateway redirects where the linker parameter would expire.


How to test your setup

  1. Enable GTM Preview mode — click Preview in the top right of your GTM workspace

  2. Complete a test booking — go through the full booking flow using your embedded widget on your website

  3. Check the GTM Preview panel on your storefront — verify that the session_bridge event fires and that the ga_client_id value matches the one from your website

  4. Verify your Google Tag — in the Preview panel, click on your Google Tag and confirm that client_id shows the bridged value (not a new auto-generated one)

  5. Open GA4 DebugView — go to Admin then DebugView in Google Analytics. Confirm that events from both your website and storefront show the same client_id, and that the purchase event appears in the same session as the widget interactions

  6. Check for duplicates — make sure the purchase event only appears once per booking in GA4


Troubleshooting

Purchases showing as "organic shopping" or wrong source in GA4? This happens when the purchase event fires in a disconnected session on the storefront. Make sure cross-domain measurement is configured (Step 1), and if you use your own GTM container, verify that the session bridging fields are set on your Google Tag (Steps 2–4).

No session_bridge event on the storefront? Make sure your website has GA4 running and creating cookies. The embedded widget reads the _ga cookie to capture the client ID. If GA4 isn't loaded on your website, there's nothing for the widget to capture.

ga_client_id is empty? Check that your GA4 tag fires before the embedded widget renders on your website. If GA4 loads after the widget, the cookies may not be set yet.

Sessions still appear separate in GA4? Verify that cross-domain measurement (Step 1) includes your storefront domain. Also confirm that the client_id field override in your Google Tag (Step 4) is correctly configured and that the Custom JavaScript variables (Step 3) return the right values.

Duplicate purchase events? Make sure you modified your existing Google Tag in Step 4 rather than creating a new one. Having two Google Tags with the same Measurement ID will cause duplicate events.

Using a cookie consent banner? If you use a consent management tool, GA4 cookies may not be set until the guest gives consent. The widget can only capture the client ID if the _ga cookie exists. Guests who decline analytics cookies won't have their sessions bridged.


Did this answer your question? If not, please reach out to us in the chat window at the bottom to the right, and we'll be happy to help.

Did this answer your question?