Use this guide to return customers to your website after a booking submission and measure submissions by location and appointment type using Google Tag Manager (GTM) and Google Analytics 4 (GA4). It covers both embedded booking forms and links to the hosted booking page.
Choose one conversion tracking route
Recommended: track on your website after the redirect. Create a dedicated thank-you page, configure a dynamic redirect, and use your website GTM container to send a GA4 event. You do not need to install GTM inside mybridal for this route. Follow the redirect setup sections below, then the final testing checklist.
Advanced: track the booking completion event inside the portal. Use this when you need tracking without a thank-you page. A linked booking page can send its own analytics event. An embedded form needs a separate message bridge if its event is to be handled by the parent website. Follow the advanced portal tracking and iframe bridge sections below, then the final testing checklist.
Use only one route to count a submission as a conversion. Sending a portal event and a thank-you event to the same conversion action can count the same submission twice.
What a successful submission means
mybridal runs completion handling after a successful appointment request or direct booking. Where the store uses requests, this means appointment request submitted. It does not mean the store has approved the request, the customer has attended, or a sale has completed. Approval and cancellation do not automatically update these browser events.
Choose an event name such as appointment_request_submitted for a request-only flow, or booking_submission for mixed flows. The redirect has no status token, so it cannot distinguish requests from direct bookings on its own. The built-in portal event name is mybridal_app_request for both flows.
Before you start
Obtain access to the website, its GTM container, the correct GA4 property, and mybridal booking settings. Agree which event counts as a conversion, which stores and appointment types are in scope, and how test traffic and repeat visits will be treated. The website developer or agency can complete the setup and testing using that access.
The implementation described here was checked against the current code. Complete an end-to-end test on the account’s deployed booking form before publishing tracking changes. No real booking was submitted while preparing this guide.
Configure the booking redirect
Select the relevant store in mybridal. Under Settings > Account, find Store Booking System URL. Its menu provides Copy URL, Copy Embed Code and Settings. You can also open the booking editor through Diary > Booking Settings.
In the booking editor, open Settings > General > Redirect URL, enter a full HTTPS URL and save using Update. A relative address such as /thank-you/ alone is not accepted.
The saved redirect is shared across the company account. It is not an isolated setting for whichever store is selected. Record the existing value before changing it. Also inspect each website embed or booking link for a redirectUrl override.
https://www.example.com/thank-you/?store={store}&type={appointmentType}
The customer is redirected in the main browser window, replacing the page containing the iframe. The code waits approximately one second after completion before requesting navigation. This delay does not guarantee that third-party analytics tags finish. A sandboxed iframe or browser navigation restriction can prevent the redirect; test the actual website embed.
Supported tokens are case sensitive
{store} inserts the selected store display name.
{storeId} inserts the internal store ID, useful for consistent reporting when names change.
{storeAbbrev} inserts the store abbreviation, if configured.
{appointmentType} inserts the selected appointment type name.
{appointmentTypeId} inserts the internal appointment type ID.
{date} inserts the appointment date as YYYY-MM-DD in the store’s time zone. This is not the submission date.
There is no booking ID, request ID or unique reference token. Unknown placeholders remain unchanged; known tokens with missing data become empty strings. Detect both cases during testing.
The names before the equals signs, such as store and type, are query parameter names chosen by the developer. The values inside braces are mybridal tokens. You can choose different parameter names, but your tracking configuration must use the same names.
Names come from Settings > Stores and Settings > Defaults > Appointment Types. Values retain capitalisation and whitespace. mybridal URL-encodes them automatically. Confirm which types are currently used for online booking; an internal appointment type is not necessarily available publicly.
Examples and a separate test redirect
An example store and appointment type
For this example, the store is named “Example Bridal” and the appointment type is “Bridal Appointment”. These are fictional names. Replace them with the exact names saved in your own mybridal account.
https://www.example.com/thank-you/?store={store}&type={appointmentType}
For that selection, the result is the following single URL. The thank-you path is an example, not confirmation that the page exists.
https://www.example.com/thank-you/?store=Example%20Bridal&type=Bridal%20Appointment
%20 represents a space and %26 represents &. URLSearchParams decodes these automatically. Trim leading or trailing whitespace for reporting if needed, and retain IDs for stable grouping. Do not decode a value a second time after URLSearchParams has decoded it.
Override the redirect for a test link or embed
The redirectUrl query parameter takes precedence over the saved account setting. Put it on the booking URL, not the parent website URL. A non-empty invalid override prevents the saved redirect from being used, so remove a broken override rather than expecting a fallback.
This JavaScript constructs a correctly encoded override. Replace both example addresses. Run it in your development tooling to generate the URL, then copy the output into the test embed or link.
const booking = new URL(
'https://portal.mybridalsoftware.com/p/STORE_SHORT_ID'
);
const target = 'https://www.example.com/thank-you-test/'
+ '?store={store}&store_id={storeId}'
+ '&type={appointmentType}&type_id={appointmentTypeId}';
booking.searchParams.set('redirectUrl', target);
console.log(booking.href);
URL.searchParams.set encodes the nested redirect URL once. Do not encode target first. For a raw iframe, replace its src with the resulting booking URL. For the generated mybridal embed, replace its data-url value and retain the supplied embed script. In HTML attributes, escape literal & separators as & where required.
Use a private test page and a test analytics destination. A test embed still submits to the live store. Use your own contact details, a free appointment type and an agreed slot; normal notifications and reminders can run. Cancel or remove the test request afterwards. A Demo store is not automatically a sandbox and may have no working public link; never use a link ending in /undefined.
Set up GTM on the thank you page
Install or reuse the website’s GTM container and Google tag connected to the intended GA4 stream. Avoid a second installation through a theme or analytics plugin. This route uses the website container only. See Google’s Google tag setup guide.
1. Pass the reporting fields
https://www.example.com/thank-you/?store={store}&store_id={storeId}&type={appointmentType}&type_id={appointmentTypeId}
Keep this as a single URL. Create GTM variables through Variables > New > URL, set Component Type to Query, and use the query keys store, store_id, type and type_id. Give the variables clear names such as URL - Store and URL - Appointment Type. Inspect their decoded values in Preview. See GTM variable types.
2. Create a restricted trigger
For a basic implementation, use a Page View trigger restricted to the exact website hostname and thank-you path. Require non-empty store_id and type_id and reject values still containing braces. Use the corresponding required name fields if you use the shorter example without IDs. Do not trigger on every page or on the booking button click.
A page-view trigger counts another event on refresh. It also cannot prove that a visitor with a copied thank-you URL submitted a booking. Decide whether that limitation is acceptable or implement the controls in the repeat visits section before publishing.
3. Send the event
Create a Google Analytics: GA4 Event tag for the intended measurement ID. Use appointment_request_submitted for a request-only setup, or the agreed submission event name. Attach the trigger above. Add these as event parameters, not user properties:
store_name = URL variable for store
store_id = URL variable for store_id
appointment_type = URL variable for type
appointment_type_id = URL variable for type_id
tracking_method = fixed value redirect
Use Preview to connect Tag Assistant, submit the test, and verify one tag firing with the expected parameters. Check GA4 DebugView with debugging enabled. Publish the GTM container only after the tests pass. See Google’s event setup instructions.
4. Make the result usable in reports
Register the required parameters as event-scoped custom dimensions in GA4 under Admin > Custom definitions. Mark the chosen event as a key event under Admin > Events. If Google Ads uses it, configure the relevant conversion action and avoid also counting a second direct Ads tag for the same outcome. See custom dimensions and key events.
Repeat visits consent and attribution
What can and cannot be deduplicated
mybridal does not currently expose a unique booking or request token in the redirect. Store, appointment type and date are not a unique key: different customers can share all three. Do not use their combination as a transaction ID or claim it guarantees one conversion per booking.
A once-per-page GTM setting does not prevent a new event after a refresh. A once-per-session rule can suppress a second legitimate request. Document whichever measurement tradeoff you choose.
For an optional website-managed guard, create a random attempt ID on the booking page and store it in sessionStorage. Add it as a static tracking_attempt query parameter inside the redirectUrl value for that attempt. On return, verify it matches the pending attempt, push the tracking event once, then consume the pending value. Generate a fresh value for a new attempt. This requires developer implementation and consent-aware storage handling; tracking_attempt is not a mybridal token.
Such a guard reduces refresh and copied-link counts in the same browser tab. It is not a server-verified booking ID and cannot guarantee delivery or deduplication across devices, cleared storage or all navigation patterns. If used, trigger GA4 from that guarded custom event instead of the basic page-view trigger. Do not run both.
Only send the fields required for reporting
The portal’s completion data can contain customer-entered information. Explicitly select permitted fields such as appointment type and store ID. Do not forward the entire booking object. Do not send customer names, email addresses, phone numbers, notes or wedding dates to GA4 or include them in redirect URLs. The old guide’s examples using those fields should be retired. See Google’s guidance on personally identifiable information.
Connect Google tags to the website’s consent management setup and test both consent states. Consent on the parent website is not automatically available inside a cross-origin booking iframe. For portal-side tracking, initialise and update consent there as well; do not assume installing GTM handles this. Use the CMP’s supported integration and consent APIs, not a Custom HTML tag to configure consent. See Google’s consent setup guidance.
Check where conversions are attributed
The redirect tokens carry booking details, not original campaign IDs, UTMs or GA client identifiers. mybridal does not automatically copy arbitrary incoming query parameters into the redirect. Preserve and test campaign attribution in the website’s analytics implementation. Static UTM values on the thank-you URL can overwrite the attribution you meant to measure.
A linked booking journey can cross domains. An embedded journey has separate parent and iframe browser contexts. Do not assume shared GA sessions or consent. Verify source and medium in a controlled campaign test as well as checking that the conversion event exists. Browser restrictions, ad blockers, closed tabs and denied consent can reduce observed events; analytics totals are not a replacement for mybridal request records.
Advanced tracking inside the booking portal
Use this route when tracking should happen without a thank-you page. The website’s GTM container cannot automatically read the cross-origin booking iframe’s data layer. Portal tracking needs a container installed in that booking context.
Install a dedicated portal container
Create a Web container for the booking portal. Open Booking Settings > Settings > Custom > On Load Code. Preserve any existing code, and add the GTM JavaScript installation snippet from your container. Save with Update. This setting is shared across the account.
The custom code is inserted into the document head. Add only the JavaScript script snippet here, not the GTM noscript iframe intended for the document body. The booking app already requires JavaScript. Do not overwrite window.dataLayer; retain it and use push. Initialise your consent solution before measurement tags. See Google’s data layer guidance.
Test using the normal public booking URL without edit=true. The editor suppresses injected custom code, so it is not a valid tracking test. Preview the portal container against the public booking URL, then test the actual embed if applicable.
Listen for the built in completion event
mybridal pushes mybridal_booking_loaded when the booking form loads and mybridal_app_request after successful submission. Loading is not a conversion. The completion payload uses the current client booking object; it is not a guaranteed server-confirmed reference-ID contract.
Create a GTM Custom Event trigger with the exact event name mybridal_app_request and regex matching off. “All Custom Events” within this trigger means all occurrences of that specified name. Do not use a wildcard trigger. See custom event triggers.
For a linked portal page, create Data Layer Variables using Version 2 for type.name and type._id. Map these to appointment_type and appointment_type_id in a GA4 Event tag, attach the completion trigger and use the portal’s consent-configured Google tag. Verify the values on the completion event in Preview.
For an iframe, the following sender and receiver sections show a custom bridge to the website container. The bridge intentionally sends only appointment type metadata. The built-in event does not explicitly add a dependable selected-store field. Do not infer the selected location from the initial embed URL if customers can switch stores. Use redirect tokens when location reporting is required.
Do not add a second completion event in On Completion Code: the built-in event already exists. That code runs before the built-in completion push, so relying on it to read the new event can fail. If fbq is installed in the portal, mybridal also calls trackCustom with the built-in event and booking object; review that integration separately before installing a pixel.
Advanced iframe bridge sender
This bridge is optional custom website integration, not a built-in mybridal postMessage feature. Use it instead of redirect conversion tracking. Leave the redirect unset for this flow to avoid navigation interrupting delivery. Do not clear a shared redirect without agreeing the effect on other stores.
In the portal GTM container, create a Custom HTML tag triggered only by mybridal_app_request. Add the code below. Replace https://www.example.com with the exact parent website origin, including www if used. An origin has no path. Avoid a wildcard target origin.
<script>
(function () {
if (window.parent === window) return;
var dl = window.dataLayer || [];
var row;
for (var i = dl.length - 1; i >= 0; i--) {
if (dl[i] && dl[i].event === 'mybridal_app_request') {
row = dl[i];
break;
}
}
if (!row || !row.type) return;
if (typeof row.type.name !== 'string' ||
typeof row.type._id !== 'string') return;
window.parent.postMessage({
source: 'mybridal-booking',
version: 1,
event: 'mybridal_app_request',
appointment_type: row.type.name,
appointment_type_id: row.type._id
}, 'https://www.example.com');
}());
</script>
The tag reads the completion payload without copying customer fields or inserting user-entered strings directly into executable code. Keep the allowed payload small. Apply the agreed tracking consent rules to the sender and recipient tags.
A separate consent-configured Google tag is not needed inside the portal if the portal container only forwards the event and the website sends it to Analytics. Do not also send a GA4 completion event from the portal container in this arrangement.
The receiving listener must be installed before submission. The example in the receiver section below assumes one directly embedded booking iframe. If your CMS nests another iframe or the embed script creates it dynamically, adapt the iframe lookup to the actual booking frame.
Advanced iframe bridge receiver
On your website, give the booking iframe the ID mybridal-booking and install this listener once before a submission is possible. It can be website code or a website GTM Custom HTML tag restricted to the booking page. Set portalOrigin to the actual final iframe origin, which may differ from its initial URL after redirects.
<script>
(function () {
if (window.__mybridalListenerInstalled) return;
window.__mybridalListenerInstalled = true;
var portalOrigin = 'https://portal.mybridalsoftware.com';
window.addEventListener('message', function (e) {
var frame = document.getElementById('mybridal-booking');
if (!frame || e.source !== frame.contentWindow ||
e.origin !== portalOrigin) return;
var d = e.data;
if (!d || typeof d !== 'object' ||
d.source !== 'mybridal-booking' || d.version !== 1 ||
d.event !== 'mybridal_app_request') return;
if (typeof d.appointment_type !== 'string' ||
typeof d.appointment_type_id !== 'string' ||
!d.appointment_type_id ||
d.appointment_type.length > 200 ||
d.appointment_type_id.length > 100) return;
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'mybridal_booking_submission',
appointment_type: d.appointment_type.trim(),
appointment_type_id: d.appointment_type_id,
tracking_method: 'iframe_message'
});
});
}());
</script>
In the website GTM container, create a Custom Event trigger named mybridal_booking_submission. Create Data Layer Variables for appointment_type, appointment_type_id and tracking_method. Map them to the GA4 Event tag, using the same agreed analytics event name as your reporting plan. The parent event name is deliberately different from the portal event name.
Checking both message origin and source ties the listener to the expected frame. Reject unrelated messages; never forward every message.data object into the data layer. This sample does not guarantee one event per booking. Test repeated messages and apply your agreed counting rules. See MDN postMessage guidance.
Test publish and troubleshoot
Complete the end to end checks
1. Record the existing redirect, custom tracking code and conversion tags. Identify the actual embed URL, selected-store switching behaviour and final iframe origin. Agree a private test page and test analytics destination.
2. Test the public form, not the settings editor. Submit an agreed free request with your own contact details. Coordinate notifications and diary use with the store. Payment flows require a separate controlled test if they are in scope; a free test does not verify paid completion.
3. Confirm that success, not a button click or failed validation, triggers the conversion. For requests, verify that no approval is needed before tracking fires. Test a failed submission and an abandoned form as negative cases.
4. For redirects, confirm top-level navigation, decoded names, IDs and the chosen date where included. Test each relevant store and at least one appointment type per store, including changing store inside the embed.
5. In GTM Preview, check exactly one intended conversion tag fires and only allowed parameters are sent. Confirm receipt in GA4 DebugView. Test consent accepted and rejected, a mobile browser, and the real production embed structure.
6. Refresh, revisit and directly open the thank-you URL. Confirm the agreed repeat-visit behaviour. For a bridge, check that wrong-origin and unrelated-frame messages are ignored. Check campaign attribution separately.
7. Publish the tested website and GTM changes. Remove temporary overrides and test tags, and cancel test requests. Retain a rollback copy of the previous configuration. Record the event name, parameters, counting limitations and owner of the tracking setup.
If something does not work
No redirect: check the full HTTPS address, saved settings, any redirectUrl override, successful submission and iframe navigation restrictions. An invalid non-empty override blocks fallback. A relative URL is not accepted.
Literal braces or blank values: check exact token spelling and case, whether the live portal supports token replacement, and whether the source field is populated. Check that a nested redirect was encoded once.
Wrong location: compare the selected store with the embed’s starting store. Use the selected-store redirect tokens. Trim names for reports, and group by IDs when names can change.
No analytics event: inspect the correct container and browser context, consent state, ad blockers and measurement ID. The parent GTM cannot see iframe events without a bridge. Use the public booking page without edit=true.
Duplicate counts: check for duplicate GTM installations, both redirect and portal tags, old On Completion Code, and more than one Google Ads conversion action. Then check refresh handling.
Using this guide with older installations
Replace the old GA4 Configuration tag instructions with the current Google tag setup. Use event parameters for booking metadata, not customer user properties. Remove the old unrestricted message listener and any forwarding of customer details. Account settings are mutable: the absence of a redirect in a previous support email is not evidence that the setting is still empty today.