Integrations

Lemon Squeezy revenue attribution and webhook setup

Lemon Squeezy returns checkout custom data with order and subscription events. Use that handoff to relate a sale to its visit, while keeping initial subscription invoices from inflating revenue.

Before you connect

Install the tracker on your verified website and confirm the first event. Open that site’s Settings → Revenue as a workspace owner or admin, choose Lemon Squeezy, and copy the webhook URL shown there. Your deployment’s settings are the authority for its origin.

Configure the webhook

  1. In Lemon Squeezy Settings → Webhooks, create a webhook with the site-specific URL below. Start with test mode and a separate analytics site. Choose a strong signing secret in Lemon Squeezy and keep the same value for AnalyticFast.
  2. For hosted tracking, the endpoint has this form:
    https://t.analyticfast.com/api/webhooks/lemonsqueezy/YOUR_SITE_ID
    Replace YOUR_SITE_ID with this site’s ID; use the exact URL from Settings if its origin differs.
  3. Subscribe to these events:
    • order_created
    • order_refunded
    • subscription_created
    • subscription_payment_success
    • subscription_payment_refunded
  4. Save the signing secret you chose, not an API key. The receiver verifies X-Signature over the unchanged request body. Lemon Squeezy does not sign a timestamp; payment and refund persistence handle duplicate deliveries.
  5. Save. Optionally enter product IDs to filter transactions. A matching product includes the entire transaction, so a filter is not product-level revenue allocation.
Screenshot placeholder: Lemon Squeezy webhook destination with the site URL and required events selected; hide all signing secrets.

Carry the visitor into checkout

Hosted checkout accepts checkout[custom][ml_visitor_id] and checkout[custom][ml_session_id]. If creating checkout on your server, use checkout_data.custom. The webhook returns custom values in meta.custom_data. See Lemon Squeezy’s custom-data documentation.

// Browser: read IDs immediately before asking your server to create checkout.
const attribution = window.analyticfast?.attribution?.() ?? {};
// Send these optional IDs with your existing checkout request.
// Server: validate them and copy only the present values into provider metadata.
// Keep prices, products and all payment credentials under server control.
// Browser helper, for your own trusted Lemon Squeezy checkout URL.
function checkoutUrlWithAttribution(checkoutUrl) {
  try {
    const url = new URL(checkoutUrl);
    if (url.protocol !== 'https:' || url.username || url.password || url.port) return checkoutUrl;
    const ids = window.analyticfast?.attribution?.() ?? {};
    if (ids.ml_visitor_id && ids.ml_session_id &&
        !url.searchParams.has('checkout[custom][ml_visitor_id]') &&
        !url.searchParams.has('checkout[custom][ml_session_id]')) {
      url.searchParams.set('checkout[custom][ml_visitor_id]', ids.ml_visitor_id);
      url.searchParams.set('checkout[custom][ml_session_id]', ids.ml_session_id);
    }
    return url.href;
  } catch { return checkoutUrl; } // Checkout must work if analytics is unavailable.
}
// Use the returned URL in your existing checkout navigation.

Use your own trusted checkout URL. When it already contains analytics identity fields, preserve the existing pair instead of mixing identities. Automatic link tagging depends on the site’s configured checkout allowlist; the explicit handoff above makes the required fields visible.

What creates revenue and refunds

Orders and renewal invoices have separate financial identities. Initial subscription invoices map to the original order and do not add a second sale. Keep subscription_created enabled: it supplies order and product mapping, including for early renewal/refund deliveries. Refund amounts are cumulative balances, so repeated or out-of-order snapshots reconcile to the refunded total rather than subtracting that total again.

Verify with a real signed test delivery

  1. On your separate test site, arrive with a unique UTM campaign and complete a sandbox payment through your actual checkout flow.
  2. Inspect the provider’s delivery log, then check the payment, its currency and its attributed visit in AnalyticFast. Test mode data should stay out of your production analytics site.
  3. Retry the same delivery and confirm no duplicate revenue. Issue a successful partial or full refund and confirm the net amount falls once. For subscriptions, test a later renewal.

The settings test button is a local parser preview. It does not contact Lemon Squeezy, verify signatures over the network or persist revenue.

Limits to check before going live

A missing subscription mapping can leave an initial-invoice refund or filtered renewal pending. Check subscription_created delivery before changing amounts by hand. A product filter uses the product information actually included in the webhook; this adapter does not fetch every line item from the provider API.

No universal historical import runs when you save a webhook. Validate new deliveries first. Keep currencies separate and compare like date ranges; an analytics revenue total is not a bank payout statement.

Official references

Setup reviewed against the current adapter and official documentation on October 31, 2026. This is an implementation guide, not a claim that a live checkout was exercised.

Add your website to AnalyticFast, then open Settings → Revenue to copy its webhook URL.

Questions, answered.

Why should I subscribe to subscription_created as well as payment events?

It maps the subscription to its original order and product. That context lets the receiver reconcile initial-invoice refunds and apply product filters to later invoices without counting the initial sale twice.

Does the test button send a real payment?

No. It is a local parser preview: it contacts no provider, verifies no signed delivery, and records no revenue. Use a separate AnalyticFast test site and your provider sandbox to check payment, renewal, retry and refund delivery.

Do I need to expose payment credentials in my website?

No. The Site ID is public. Webhook signing secrets belong in the Revenue settings, and payment API keys belong on your server. Only optional visitor and session IDs travel from the browser to your checkout code.