> For the complete documentation index, see [llms.txt](https://docs.humblytics.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.humblytics.com/how-to-track-purchase-events/beehiiv-revenue-tracking-and-split-testing.md).

# beehiiv – Revenue Tracking & Split Testing

Track paid beehiiv subscriptions in Humblytics and A/B test your subscribe pages to see which plan or offer earns the most.

beehiiv takes payments through Stripe, but the checkout doesn't tell Humblytics who paid. To link the two, a small Google Tag Manager tag sends the subscriber's email to Humblytics. When the matching Stripe payment arrives, Humblytics credits it to that visit.

## Before you start

1. **Install Humblytics with Google Tag Manager.** beehiiv doesn't let you add scripts directly. Follow the [Google Tag Manager guide](/how-to-get-started/google-tag-manager.md) and confirm the tag fires on your publication.
2. **Connect the Stripe account behind your beehiiv publication.** Go to **Connect → Connectors** and click **Connect** next to **Stripe**. See [Stripe setup](/how-to-track-purchase-events/stripe.md).

## Step 1: Add the email capture tag

1. In Google Tag Manager, click **New Tag** and name it `Humblytics – beehiiv Email Capture`.
2. Click **Tag Configuration → Custom HTML** and paste this script exactly as written:

```html
<script>
(function () {
  var EMAIL_SELECTOR = "#placeholder, input[type='email'], input[placeholder*='email' i]";
  var EMAIL_STORAGE_KEYS = ["email"];

  var _latestEmail = "";
  var _hasTracked = false;

  function _cleanEmail(value) {
    if (value == null) return "";

    var str = String(value).trim();

    // Handles localStorage values like: "\"max@humblytics.com\""
    try {
      var parsed = JSON.parse(str);
      if (typeof parsed === "string") {
        str = parsed;
      }
    } catch (e) {}

    return String(str || "").trim().toLowerCase();
  }

  function _isValidEmail(email) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(email);
  }

  function _getEmailFromUrl() {
    var email = new URLSearchParams(window.location.search).get("email");
    email = _cleanEmail(email);

    return _isValidEmail(email) ? email : "";
  }

  function _getEmailFromLocalStorage() {
    for (var i = 0; i < EMAIL_STORAGE_KEYS.length; i++) {
      var key = EMAIL_STORAGE_KEYS[i];
      var value = localStorage.getItem(key);
      var email = _cleanEmail(value);

      if (_isValidEmail(email)) {
        return email;
      }
    }

    return "";
  }

  function _getEmailFromInput() {
    var input = document.querySelector(EMAIL_SELECTOR);
    if (!input) return "";

    var email = _cleanEmail(input.value);

    return _isValidEmail(email) ? email : "";
  }

  function _getBestEmail() {
    return (
      _getEmailFromUrl() ||
      _getEmailFromLocalStorage() ||
      _getEmailFromInput() ||
      _latestEmail
    );
  }

  function _trackEmail(email) {
    var _emailToTrack = _cleanEmail(email);

    if (!_isValidEmail(_emailToTrack)) return;
    if (_hasTracked) return;

    _hasTracked = true;

    var _trackHmblEmail = function(humblytics) {
      humblytics.trackCustomer(_emailToTrack);
    };

    if (
      window.Humblytics &&
      typeof window.Humblytics.trackCustomer === "function"
    ) {
      window.Humblytics.trackCustomer(_emailToTrack);
      return;
    }

    if (typeof window.HumblyticsOnReady === "function") {
      window.HumblyticsOnReady(_trackHmblEmail);
      return;
    }

    window.HumblyticsCallbacks = window.HumblyticsCallbacks || [];
    window.HumblyticsCallbacks.push(_trackHmblEmail);
  }

  function _checkAndTrack() {
    _trackEmail(_getBestEmail());
  }

  // Track immediately if email is already in the URL.
  _checkAndTrack();

  // Watch for beehiiv saving the email to localStorage.
  var _originalSetItem = localStorage.setItem.bind(localStorage);

  localStorage.setItem = function (key, value) {
    var result = _originalSetItem(key, value);

    if (EMAIL_STORAGE_KEYS.indexOf(key) !== -1) {
      var email = _cleanEmail(value);

      if (_isValidEmail(email)) {
        _trackEmail(email);
      }
    }

    return result;
  };

  // Backup poll in case the email was already saved before this script ran.
  var _pollRef = setInterval(function () {
    if (_hasTracked) {
      clearInterval(_pollRef);
      return;
    }

    _checkAndTrack();
  }, 250);

  setTimeout(function () {
    clearInterval(_pollRef);
  }, 30000);

  // Store typed email value, but do not track until Continue / Enter.
  document.addEventListener(
    "input",
    function (event) {
      if (
        event.target &&
        event.target.matches &&
        event.target.matches(EMAIL_SELECTOR)
      ) {
        _latestEmail = _cleanEmail(event.target.value);
      }
    },
    true
  );

  document.addEventListener(
    "change",
    function (event) {
      if (
        event.target &&
        event.target.matches &&
        event.target.matches(EMAIL_SELECTOR)
      ) {
        _latestEmail = _cleanEmail(event.target.value);
      }
    },
    true
  );

  // Track when user clicks Continue.
  document.addEventListener(
    "pointerdown",
    function (event) {
      var button = event.target.closest && event.target.closest("button");
      if (!button) return;

      var text = String(button.innerText || button.textContent || "")
        .trim()
        .toLowerCase();

      if (text === "continue") {
        _checkAndTrack();
      }
    },
    true
  );

  document.addEventListener(
    "click",
    function (event) {
      var button = event.target.closest && event.target.closest("button");
      if (!button) return;

      var text = String(button.innerText || button.textContent || "")
        .trim()
        .toLowerCase();

      if (text === "continue") {
        _checkAndTrack();
      }
    },
    true
  );

  // Track if user presses Enter in the email field.
  document.addEventListener(
    "keydown",
    function (event) {
      if (
        event.key === "Enter" &&
        event.target &&
        event.target.matches &&
        event.target.matches(EMAIL_SELECTOR)
      ) {
        _latestEmail = _cleanEmail(event.target.value);
        _checkAndTrack();
      }
    },
    true
  );
})();
</script>
```

3. Under **Triggering**, choose **All Pages**.
4. Click **Save**, then **Submit** and **Publish** the container.

{% hint style="warning" %}
Don't edit the script. Its settings match beehiiv's subscribe flow, and changes can stop emails from being captured.
{% endhint %}

## Step 2: Check that it works

1. Open your publication in a private window.
2. Subscribe to a paid plan with a real email and complete checkout.
3. In Humblytics, check **Observe → Attribution → Recent Attributed Transactions**. The payment should appear within a few minutes.

Nothing after 10 minutes? Check that:

* Stripe shows **Connected** in **Connect → Connectors**.
* The GTM container is **published**, not just saved.
* Both Humblytics tags fire on the subscribe page. Use GTM's **Preview** mode.

## Step 3: A/B test your subscribe pages

1. In beehiiv, create a separate page for each version you want to test. For example, one page that leads with the monthly plan and one that leads with the annual plan. Each needs its own URL.
2. In Humblytics, go to **Optimize → Experiments** and click **New Experiment**.
3. For the goal, choose **Revenue**.
4. For the test type, choose **Page-Level Testing** and add your original page as the control and the other page as the variant.
5. Finish setup and launch the test.

Humblytics splits visitors between the pages and credits each paid subscription to the page that visitor saw. Open the test in **Experiments** to see revenue and confidence for each version.

## How it works

1. A visitor lands on your subscribe page and sees one version.
2. They enter their email, and the tag passes it to Humblytics.
3. They pay through Stripe.
4. Humblytics finds the Stripe payment with the same email and credits it to that visit, and to that test variant.
