Sign up

Revenue

Connect Lemon Squeezy

On this page

Lemon Squeezy sends orders and subscription payments to a webhook signed with a secret MetricRaven generates. Checkout custom data links each order to the visit that started it.

Set up the webhook

  1. Open Settings → Integrations → Lemon Squeezy

    Choose Set up Lemon Squeezy. Copy the webhook URL and the signing secret; the secret is shown once.

  2. Add a webhook in Lemon Squeezy

    In Lemon Squeezy, open Settings → Webhooks and choose +. Paste the URL as the callback URL and the secret as the signing secret.

  3. Select the payment events

    Select order_created, order_refunded, subscription_payment_success and subscription_payment_refunded. For subscription goals (trial started, cancelled, ended), also select subscription_created, subscription_updated, subscription_cancelled, subscription_resumed, subscription_expired, subscription_paused and subscription_unpaused. Then save.

Pass the visitor ID at checkout

Lemon Squeezy returns checkout custom data in each webhook's meta.custom_data. Use these keys:

KeyValue
metricraven_visitor_idThe visitor ID from getVisitorId() (24 or 32 hex characters).
metricraven_user_idOptional. Your signed-in user's ID, set on the server, matching the Identify API.
metricraven_planOptional. A plan name for reports; otherwise the product from the order's first item is used.
  1. Read the visitor ID in the browser

    The MetricRaven script exposes window.analytics.getVisitorId(). Read it when checkout starts and send it to your checkout code:

    script.js
    // Read this in the visitor's browser when they start checkout.
    const visitorId = window.analytics?.getVisitorId() ?? null;
    // Send visitorId to your own checkout endpoint alongside its usual request data.
    // Use the authenticated user ID from your backend; do not trust a browser-supplied user ID.
  2. Add it to the checkout

    For a checkout link or Lemon.js overlay, add a query parameter:

    Checkout link
    // Browser: add the visitor ID to a Lemon Squeezy checkout link.
    const url = new URL("https://YOUR_STORE.lemonsqueezy.com/buy/YOUR_VARIANT_ID");
    const visitorId = window.analytics?.getVisitorId();
    if (visitorId) url.searchParams.set("checkout[custom][metricraven_visitor_id]", visitorId);
    window.location.href = url.toString();

    When you create checkouts on your server, put it in checkout_data.custom:

    Checkouts API
    // Server: create a checkout with the Lemon Squeezy API.
    // visitorId came from your browser; user is your authenticated user (or null for guests).
    const custom = {
      ...(visitorId ? { metricraven_visitor_id: visitorId } : {}),
      ...(user ? { metricraven_user_id: user.id } : {}),
    };
    const response = await fetch("https://api.lemonsqueezy.com/v1/checkouts", {
      method: "POST",
      headers: {
        Accept: "application/vnd.api+json",
        "Content-Type": "application/vnd.api+json",
        Authorization: `Bearer ${process.env.LEMONSQUEEZY_API_KEY}`,
      },
      body: JSON.stringify({
        data: {
          type: "checkouts",
          attributes: { checkout_data: { email: user?.email, custom } },
          relationships: {
            store: { data: { type: "stores", id: "YOUR_STORE_ID" } },
            variant: { data: { type: "variants", id: "YOUR_VARIANT_ID" } },
          },
        },
      }),
    });
    const { data } = await response.json();
    // Redirect the visitor to data.attributes.url

MetricRaven links each payment to a visit using the first of these that is available:

  1. The metricraven_visitor_id passed through checkout.
  2. The metricraven_user_id (or the provider's customer external ID), matched to a visitor you linked with the Identify API.
  3. An earlier payment from the same provider customer, so renewals keep the original attribution.
  4. The checkout email, matched to an email you sent to the Identify API.
  5. The checkout email, matched to a browser that called payment({ email }) within an hour of the payment.

Can't set custom data on your checkout? See Other Lemon Squeezy methods to match by the buyer's email.

Verify it works

After a delivery, the Lemon Squeezy card in Settings → Integrations shows Connected, the number of verified events and the last event type. A live payment appears in Revenue in its own currency; with a visitor ID it is credited to that visit's source in Traffic.

If the card says a delivery was rejected, the signing secret doesn't match. Paste it again or generate a replacement.

Details

  • The first payment of a subscription is counted from its order. Later subscription_payment_success invoices with billing_reason: renewal are added as renewals and keep the customer's attribution.
  • Amounts are converted from cents using each currency's decimals; total includes tax. Refunds use Lemon Squeezy's cumulative refunded_amount.
  • Test mode orders are acknowledged but not counted.
  • Signature: X-Signature is the hex HMAC-SHA256 of the raw request body with your signing secret.
  • Each delivery is verified with an HMAC signature over the exact request body, compared in constant time. Unsigned or altered requests are rejected with 401 and never change revenue.
  • Redelivered events are recognized and ignored, so provider retries never add a payment twice. Refunds reduce the original payment and never exceed it.
  • The signing secret is stored encrypted, bound to this site, and never shown in reports, share links or logs. Disconnecting deletes it; imported revenue stays in your reports.
  • Free trials and zero-amount orders are not revenue. A subscription cancellation is not a refund: revenue already paid stays.
  • Webhooks deliver new events only. Payments from before you connected are not imported, and live MRR is currently available for Stripe only.
  • Use one revenue path per purchase: don't also send the same orders through the Payments API.