> ## 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.

# Order Tracking

> Track conversions by adding the order tracking tag or pixel to your order confirmation page

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 />

## Overview

The order tracking tag captures conversion data when a customer completes a purchase. Place it on your order confirmation page alongside the [referrer tag](/developer-docs/integration/entry-points/referrer), or use it standalone if you only need conversion tracking without the referral overlay.

## Integration

<SignInNotice />

<Tabs>
  <Tab title="JavaScript tag">
    Add this snippet to the bottom of the `<body>` tag on your order confirmation page:

    <CodeGroup>
      ```html Demo theme={null}
      <!-- Begin Mention Me order tracking integration -->
      <script type="text/javascript"
        src="https://tag-demo.mention-me.com/api/v2/order/YOUR_PARTNER_CODE?email=<INSERT_EMAIL>&order_number=<INSERT_ORDER_NUMBER>&order_subtotal=<INSERT_ORDER_SUBTOTAL>&order_currency=<INSERT_ORDER_CURRENCY>&coupon_code=<INSERT_COUPON_CODE>">
      </script>
      <!-- End Mention Me order tracking integration -->
      ```

      ```html Live theme={null}
      <!-- Begin Mention Me order tracking integration -->
      <script type="text/javascript"
        src="https://tag.mention-me.com/api/v2/order/YOUR_PARTNER_CODE?email=<INSERT_EMAIL>&order_number=<INSERT_ORDER_NUMBER>&order_subtotal=<INSERT_ORDER_SUBTOTAL>&order_currency=<INSERT_ORDER_CURRENCY>&coupon_code=<INSERT_COUPON_CODE>">
      </script>
      <!-- End Mention Me order tracking integration -->
      ```
    </CodeGroup>
  </Tab>

  <Tab title="1x1 tracking pixel">
    If you cannot include the JavaScript tag on your order confirmation page (for example, if you're including it on a page without access to order details or with restricted scripting), you can use this alternative 1x1 tracking pixel.

    <CodeGroup>
      ```html Demo theme={null}
      <!-- Begin Mention Me order tracking pixel -->
      <img src="https://tag-demo.mention-me.com/api/v2/orderpixel/YOUR_PARTNER_CODE/pixel.gif?email=<INSERT_EMAIL>&order_number=<INSERT_ORDER_NUMBER>&order_subtotal=<INSERT_ORDER_SUBTOTAL>&order_currency=<INSERT_ORDER_CURRENCY>&coupon_code=<INSERT_COUPON_CODE>"
        width="1" height="1" style="display:none" />
      <!-- End Mention Me order tracking pixel -->
      ```

      ```html Live theme={null}
      <!-- Begin Mention Me order tracking pixel -->
      <img src="https://tag.mention-me.com/api/v2/orderpixel/YOUR_PARTNER_CODE/pixel.gif?email=<INSERT_EMAIL>&order_number=<INSERT_ORDER_NUMBER>&order_subtotal=<INSERT_ORDER_SUBTOTAL>&order_currency=<INSERT_ORDER_CURRENCY>&coupon_code=<INSERT_COUPON_CODE>"
        width="1" height="1" style="display:none" />
      <!-- End Mention Me order tracking pixel -->
      ```
    </CodeGroup>

    The pixel returns:

    * **HTTP 204** (No Content) if the request was processed successfully
    * **HTTP 404** (Not Found) if there is a problem with the parameters

    In neither case will it cause an error or anything visible on the page.
  </Tab>
</Tabs>

## Parameters

Both the JavaScript tag and the tracking pixel accept the same parameters.

### Mandatory

<ResponseField name="email" type="string" required>
  The customer's email address. Please URL encode. Example: `doreen@example.com`
</ResponseField>

<ResponseField name="order_number" type="string" required>
  The unique order identifier from your system. Has a maximum of 50 characters. Example: `ORD-12345`
</ResponseField>

<ResponseField name="order_subtotal" type="string" required>
  The order subtotal (excluding VAT/taxes and shipping) in the currency indicated by the `order_currency` parameter. If you can't remove VAT, let us know. Example: `49.99`
</ResponseField>

<ResponseField name="order_currency" type="string" required>
  The three character (ISO 4217) currency code that the order total is in. Example: `GBP`
</ResponseField>

<ResponseField name="coupon_code" type="string" required>
  The coupon code used by the customer (if any). Example: `SAVE10`
</ResponseField>

### Recommended

<ResponseField name="customer_id" type="string">
  The unique customer identifier from your system. Example: `CUST-789`
</ResponseField>

<ResponseField name="order_item_count" type="string">
  The number of items in the basket (if applicable). This helps us understand the type of purchase and can be a signal of advocacy. Example: `3`
</ResponseField>

<ResponseField name="order_discount_amount" type="string">
  The discount amount — our assumption is this has already been taken off the order subtotal provided. This allows us to calculate the Cost Per Acquisition for referral. Example: `5.00`
</ResponseField>

<ResponseField name="segment" type="string">
  String representing a customer segment (for example one of: men, women) used to pass segmentation data about your customer to us. Has a maximum of 50 characters. Example: `vip`
</ResponseField>

<ResponseField name="order_is_subscription" type="string">
  Whether this order is for a subscription (recurring). This helps us qualify and categorise the revenue. Example: `true`
</ResponseField>

<ResponseField name="order_is_gift" type="string">
  Whether this order is a gift (being bought for someone else). Gifts can be signals of advocacy. Example: `false`
</ResponseField>

<Accordion title="Optional parameters">
  <ResponseField name="custom_field" type="string">
    Any piece of custom data you wish to pass to us.
  </ResponseField>

  <ResponseField name="address_line1" type="string">
    The customer's address line 1 (if known).
  </ResponseField>

  <ResponseField name="address_line2" type="string">
    The customer's address line 2 (if known).
  </ResponseField>

  <ResponseField name="address_city" type="string">
    The customer's address city (if known).
  </ResponseField>

  <ResponseField name="address_county" type="string">
    The customer's address county (if known).
  </ResponseField>

  <ResponseField name="address_postcode" type="string">
    The customer's address postcode (if known).
  </ResponseField>

  <ResponseField name="address_country" type="string">
    The customer's address country (if known).
  </ResponseField>

  <ResponseField name="custom_share_field" type="string">
    A piece of text you can use to pass into sharing widgets, such as what the user bought or how much they saved.
  </ResponseField>

  <ResponseField name="order_date" type="string">
    The date and time the order was placed. The format must be in ISO 8601 format and URL encoded. If not provided or invalid, we will use the date and time at the point we received the tag.
  </ResponseField>

  <ResponseField name="mm_encrypted" type="string">
    In some cases it may be desirable to ensure that email addresses and other information is not in clear text. See [encrypted parameters](/developer-docs/integration/encrypted-parameters).
  </ResponseField>

  <ResponseField name="locale" type="string">
    Locale code (e.g. `en_GB`).
  </ResponseField>
</Accordion>

## Verb Aliases in the URL

The verb in the URL path (`order`) can be replaced with `booking`, `signup`, or `transaction`, matching whichever term best describes the action on your site. All four verbs behave identically.

The same applies to the pixel form: `orderpixel` can be replaced with `bookingpixel`, `signuppixel`, or `transactionpixel`. Each pixel verb still ends with `/pixel.gif`.

For example, a signup pixel would look like:

```html theme={null}
<img src="https://tag.mention-me.com/api/v2/signuppixel/YOUR_PARTNER_CODE/pixel.gif?email=<INSERT_EMAIL>&signup_number=<INSERT_SIGNUP_NUMBER>&transaction_subtotal=<INSERT_TRANSACTION_SUBTOTAL>&transaction_currency=<INSERT_TRANSACTION_CURRENCY>"
  width="1" height="1" style="display:none" />
```

## Parameter Aliases for Non-Ecommerce

If your business tracks bookings or signups rather than orders, you can use alternative parameter names. The tag behaviour is identical — only the parameter names differ.

<Accordion title="Booking aliases">
  <ResponseField name="booking_number" type="string">
    Alias for `order_number`.
  </ResponseField>

  <ResponseField name="booking_subtotal" type="string">
    Alias for `order_subtotal`.
  </ResponseField>

  <ResponseField name="booking_currency" type="string">
    Alias for `order_currency`.
  </ResponseField>

  <ResponseField name="booking_date" type="string">
    Alias for `order_date`.
  </ResponseField>

  <ResponseField name="booking_item_count" type="string">
    Alias for `order_item_count`.
  </ResponseField>

  <ResponseField name="booking_is_subscription" type="string">
    Alias for `order_is_subscription`.
  </ResponseField>

  <ResponseField name="booking_is_gift" type="string">
    Alias for `order_is_gift`.
  </ResponseField>

  <ResponseField name="booking_discount_amount" type="string">
    Alias for `order_discount_amount`.
  </ResponseField>
</Accordion>

<Accordion title="Signup aliases">
  <ResponseField name="signup_number" type="string">
    Alias for `order_number`.
  </ResponseField>

  <ResponseField name="transaction_subtotal" type="string">
    Alias for `order_subtotal`.
  </ResponseField>

  <ResponseField name="transaction_currency" type="string">
    Alias for `order_currency`.
  </ResponseField>

  <ResponseField name="signup_date" type="string">
    Alias for `order_date`.
  </ResponseField>

  <ResponseField name="transaction_item_count" type="string">
    Alias for `order_item_count`.
  </ResponseField>

  <ResponseField name="transaction_is_subscription" type="string">
    Alias for `order_is_subscription`.
  </ResponseField>

  <ResponseField name="transaction_is_gift" type="string">
    Alias for `order_is_gift`.
  </ResponseField>

  <ResponseField name="signup_discount_amount" type="string">
    Alias for `order_discount_amount`.
  </ResponseField>
</Accordion>
