> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mention-me.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Referrer Tag – Google Tag Manager

> Deploy the Mention Me referrer tag on your order confirmation page using GTM

export const SignInNotice = ({message = "To see code snippets pre-filled with your partner code"}) => {
  return <div data-mm-sign-in style={{
    display: "none"
  }}>
      <Info>
        {message}, <a data-mm-sign-in-link href="https://app.mention-me.com/sign-in"><strong>sign in</strong></a>.
      </Info>
    </div>;
};

export const MerchantContext = () => {
  const PARTNER_STORAGE_KEY = "mm-docs-partner";
  const escapeHtml = str => str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
  const getStoredPartner = () => {
    try {
      const raw = localStorage.getItem(PARTNER_STORAGE_KEY);
      if (!raw) return null;
      const data = JSON.parse(raw);
      if (data && data.partnerCode) return data;
    } catch (e) {}
    return null;
  };
  const setStoredPartner = data => {
    try {
      localStorage.setItem(PARTNER_STORAGE_KEY, JSON.stringify(data));
    } catch (e) {}
  };
  const clearStoredPartner = () => {
    try {
      localStorage.removeItem(PARTNER_STORAGE_KEY);
    } catch (e) {}
  };
  const captureQueryParams = () => {
    const params = new URLSearchParams(window.location.search);
    const partner = params.get("partner");
    const name = params.get("name");
    if (partner) {
      const data = {
        partnerCode: partner
      };
      if (name) data.displayName = name;
      setStoredPartner(data);
      params.delete("partner");
      params.delete("name");
      const qs = params.toString();
      const clean = window.location.pathname + (qs ? "?" + qs : "") + window.location.hash;
      window.history.replaceState(null, "", clean);
      return data;
    }
    return getStoredPartner();
  };
  const isLocal = typeof window !== "undefined" && window.location.hostname === "localhost";
  const API_BASE = isLocal ? "http://localhost:3001" : "https://app.mention-me.com";
  const SIGN_IN_BASE = isLocal ? "http://localhost:3001/sign-in" : "https://app.mention-me.com/sign-in";
  const MERCHANT_WINDOW_KEY = "__mmMerchantContext";
  const publishMerchant = data => {
    window[MERCHANT_WINDOW_KEY] = data;
    window.dispatchEvent(new CustomEvent("mm-merchant-ready", {
      detail: data
    }));
  };
  const clearMerchant = () => {
    delete window[MERCHANT_WINDOW_KEY];
  };
  const BADGE_ID = "mm-partner-badge";
  const showPartnerBadge = (partnerData, source) => {
    if (document.getElementById(BADGE_ID)) return;
    const nav = document.getElementById("navigation-items");
    if (!nav || !nav.parentNode) return;
    const badge = document.createElement("div");
    badge.id = BADGE_ID;
    badge.className = "mm-partner-badge";
    const hasName = partnerData.displayName && partnerData.displayName !== partnerData.partnerCode;
    const heading = hasName ? escapeHtml(partnerData.displayName) : escapeHtml(partnerData.partnerCode);
    const code = hasName ? escapeHtml(partnerData.partnerCode) : "";
    const codeStr = code ? ' <span class="mm-partner-badge-code">' + code + '</span>' : '';
    const resetTitle = source === "clerk" ? "Sign out and switch account" : "Clear personalisation";
    badge.innerHTML = '<div class="mm-partner-badge-inner">' + '<span class="mm-partner-badge-content">' + '<span class="mm-partner-badge-context">Personalised for </span>' + '<span class="mm-partner-badge-name">' + heading + '</span>' + codeStr + '</span>' + '<button class="mm-partner-badge-reset" title="' + resetTitle + '">&times;</button>' + '</div>';
    nav.parentNode.insertBefore(badge, nav);
    badge.querySelector(".mm-partner-badge-reset").addEventListener("click", e => {
      e.preventDefault();
      e.stopPropagation();
      if (source === "clerk" && window.Clerk && window.Clerk.signOut) {
        clearMerchant();
        const redirect = SIGN_IN_BASE + "?redirect_url=" + encodeURIComponent(window.location.href);
        window.Clerk.signOut().then(() => {
          window.location.href = redirect;
        }).catch(() => {
          window.location.reload();
        });
        return;
      }
      clearStoredPartner();
      window.location.reload();
    });
  };
  const removePartnerBadge = () => {
    const el = document.getElementById(BADGE_ID);
    if (el) el.remove();
  };
  const showNotices = () => {
    document.querySelectorAll("[data-mm-sign-in]").forEach(el => {
      el.style.display = "";
    });
    const redirect = SIGN_IN_BASE + "?redirect_url=" + encodeURIComponent(window.location.href);
    document.querySelectorAll("[data-mm-sign-in-link]").forEach(a => {
      a.href = redirect;
    });
  };
  const hideNotices = () => {
    document.querySelectorAll("[data-mm-sign-in]").forEach(el => {
      el.style.display = "none";
    });
  };
  const walkCodeBlocks = replacements => {
    document.querySelectorAll("pre code").forEach(codeEl => {
      let html = codeEl.innerHTML;
      if (replacements) {
        for (const [from, to] of replacements) {
          html = html.split(from).join(escapeHtml(to));
        }
      }
      const paramStyle = "color:inherit;filter:brightness(110%);font-weight:bold;text-decoration:underline;cursor:pointer";
      html = html.replace(/(\?|&amp;)([a-z_]+)=/g, (match, prefix, name) => {
        const slug = name.replace(/_/g, "-");
        const target = document.getElementById("param-" + slug);
        if (!target) return match;
        return prefix + '<a href="#param-' + slug + '" style="' + paramStyle + '">' + name + "</a>=";
      });
      codeEl.innerHTML = html;
    });
  };
  const observeTabSwitches = callback => {
    let processing = false;
    const observer = new MutationObserver(() => {
      if (processing) return;
      processing = true;
      setTimeout(() => {
        callback();
        processing = false;
      }, 50);
    });
    setTimeout(() => {
      const tabs = document.querySelector("[role='tablist']");
      if (tabs && tabs.parentElement) {
        observer.observe(tabs.parentElement, {
          attributes: true,
          subtree: true,
          childList: true
        });
      }
    }, 500);
  };
  const pollForClerk = (onReady, onTimeout) => {
    let n = 0;
    const tick = () => {
      if (window.Clerk && window.Clerk.loaded) {
        return onReady(window.Clerk);
      }
      if (++n < 40) {
        setTimeout(tick, 250);
      } else {
        onTimeout();
      }
    };
    setTimeout(tick, 100);
  };
  const fetchMerchantFromAPI = () => {
    return fetch(API_BASE + "/api/docs/context", {
      credentials: "include",
      cache: "no-store"
    }).then(res => res.ok ? res.json() : null);
  };
  useEffect(() => {
    let cancelled = false;
    const urlPartner = captureQueryParams();
    const onPersonalised = (merchant, source) => {
      if (cancelled) return;
      const replacements = [["YOUR_PARTNER_CODE", merchant.partnerCode]];
      if (merchant.displayName) {
        replacements.push(["YOUR_TRADING_NAME", merchant.displayName]);
      }
      hideNotices();
      showPartnerBadge(merchant, source);
      walkCodeBlocks(replacements);
      observeTabSwitches(() => walkCodeBlocks(replacements));
    };
    const onUnauth = () => {
      if (cancelled) return;
      clearMerchant();
      removePartnerBadge();
      showNotices();
      walkCodeBlocks(null);
      observeTabSwitches(() => walkCodeBlocks(null));
    };
    const tryUrlFallback = () => {
      if (cancelled) return;
      if (urlPartner) {
        onPersonalised(urlPartner, "url");
      } else {
        onUnauth();
      }
    };
    if (!document.cookie.includes("__client_uat")) {
      tryUrlFallback();
      return;
    }
    pollForClerk(clerk => {
      if (cancelled) return;
      if (!clerk.session) {
        tryUrlFallback();
        return;
      }
      fetchMerchantFromAPI().then(data => {
        if (data) {
          publishMerchant(data);
          onPersonalised(data, "clerk");
        } else {
          tryUrlFallback();
        }
      }).catch(() => tryUrlFallback());
    }, () => {
      if (!cancelled) tryUrlFallback();
    });
    return () => {
      cancelled = true;
    };
  }, []);
  return null;
};

<MerchantContext />

This guide covers deploying the [Referrer tag](/developer-docs/integration/entry-points/referrer) through Google Tag Manager (GTM) instead of hard-coding it into your page template.

<Note>
  GTM manages the `<script>` tag only. The `mmWrapper` placeholder div still needs to be added directly to your order confirmation page template - GTM tags fire after the page has loaded, so they can populate a div but can't reliably guarantee it exists first.
</Note>

<Steps>
  <Step title="Add the wrapper div to your page template">
    In your order confirmation page's HTML, add:

    ```html theme={null}
    <!-- Begin Mention Me referrer placeholder div -->
    <div id="mmWrapper"></div>
    <!-- End Mention Me referrer placeholder div -->
    ```
  </Step>

  <Step title="Create the data layer push">
    On the order confirmation page, push the order details to `dataLayer` before the GTM container snippet fires (or via your existing checkout data layer event):

    ```html theme={null}
    <script>
      window.dataLayer = window.dataLayer || [];
      dataLayer.push({
        event: 'mentionMeReferrer',
        mmFirstname: 'Franklin',
        mmSurname: 'McFly',
        mmEmail: 'franklinmcfly559@mention-me.com',
        mmOrderNumber: '8494024521',
        mmOrderSubtotal: '106.75',
        mmOrderCurrency: 'GBP',
        mmLocale: 'en_GB',
        mmCouponCode: 'ABC4577'
      });
    </script>
    ```
  </Step>

  <Step title="Create Data Layer Variables in GTM">
    In GTM, create a **Data Layer Variable** for each field you pushed above (`mmFirstname`, `mmSurname`, `mmEmail`, `mmOrderNumber`, `mmOrderSubtotal`, `mmOrderCurrency`, `mmLocale`, `mmCouponCode`).
  </Step>

  <Step title="Create the Custom HTML tag">
    Add a new **Custom HTML** tag in GTM:

    <SignInNotice />

    ```html theme={null}
    <!-- Begin Mention Me referrer integration -->
    <script type="text/javascript"
      src="https://tag.mention-me.com/api/v2/referreroffer/YOUR_PARTNER_CODE?firstname={{mmFirstname}}&surname={{mmSurname}}&email={{mmEmail}}&order_number={{mmOrderNumber}}&order_subtotal={{mmOrderSubtotal}}&order_currency={{mmOrderCurrency}}&situation=postpurchase&locale={{mmLocale}}&coupon_code={{mmCouponCode}}">
    </script>
    <!-- End Mention Me referrer integration -->
    ```

    Use `tag-demo.mention-me.com` while testing, and switch to `tag.mention-me.com` only once you've verified the integration end-to-end.
  </Step>

  <Step title="Set the trigger">
    Create a **Custom Event** trigger that fires on the `mentionMeReferrer` event pushed in step 2, scoped to the order confirmation page.
  </Step>

  <Step title="Preview and publish">
    Use GTM's Preview mode to confirm the tag fires on the order confirmation page and the overlay renders correctly, then publish the container.
  </Step>
</Steps>

<Tip>
  All mandatory and optional parameters are documented on the [Referrer tag](/developer-docs/integration/entry-points/referrer) page - map any of them to data layer variables the same way.
</Tip>

## Troubleshooting

<AccordionGroup>
  <Accordion title="The tag doesn't fire">
    Use GTM's Preview mode to confirm the trigger condition is being met and the data layer event is firing before the tag executes. Check the browser console for errors from the Mention Me tag - see [common tag integration errors](/developer-docs/integration/reference/faqs#common-tag-integration-errors).
  </Accordion>

  <Accordion title="The overlay shows blank or missing data">
    Confirm each Data Layer Variable in GTM matches the key names pushed to `dataLayer` exactly (case-sensitive), and that the values are URL encoded where needed.
  </Accordion>
</AccordionGroup>
