{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "ProductLense blog and changelog",
  "home_page_url": "https://productlense.app",
  "feed_url": "https://productlense.app/feed.json",
  "description": "ProductLense is product analytics that shows which users pay you and which channels make a profit.",
  "language": "en",
  "authors": [
    {
      "name": "Sanni Heruwala",
      "url": "https://productlense.app/about"
    }
  ],
  "items": [
    {
      "id": "https://productlense.app/blog/how-we-compute-profit-per-channel",
      "url": "https://productlense.app/blog/how-we-compute-profit-per-channel",
      "title": "How we compute profit per channel",
      "summary": "The exact method behind ProductLense’s profit per channel: the attribution chain, net revenue after fees and refunds, ad spend joins, CAC, ROAS and payback.",
      "content_html": "<p>Profit per channel is the net revenue attributed to an acquisition channel, after processor fees and refunds, minus what you spent on that channel in the same period. This post explains every step ProductLense takes to compute it, and where its limits are. The short reference version is in the <a href=\"/docs/get-started/how-productlense-counts\">methodology docs</a>.</p>\n<h2 id=\"step-1-record-the-first-touch\">Step 1: record the first touch</h2>\n<p>Every visitor’s first event stores where they came from: the channel, <code>utm_source</code>, <code>utm_medium</code>, <code>utm_campaign</code>, the referrer domain, the landing page and the time. Channels are classified on arrival: click ids or paid mediums (<code>cpc</code>, <code>ppc</code>, <code>paid</code>, <code>paidsocial</code>, <code>display</code>) are <code>paid</code>, <code>utm_medium=email</code> is <code>email</code>, and the referrer decides between AI assistant, social, organic search, referral and direct.</p>\n<p>Because ProductLense is cookieless, an anonymous visitor is one browser on one day. That sounds limiting, but for revenue it rarely matters, because of step 2.</p>\n<h2 id=\"step-2-link-the-visitor-to-a-person\">Step 2: link the visitor to a person</h2>\n<p>When someone signs up or logs in, your code calls:</p>\n<div class=\"code-block\"><span class=\"code-lang\">js</span><pre><code class=\"language-js\">window.productlense('identify', 'user_123', { email: 'a@b.com' });</code></pre></div>\n<p>From that moment, the visitor is linked to <code>user_123</code>. Every later visit, on any day, joins the same person, and the first touch stays the one recorded before signup.</p>\n<h2 id=\"step-3-link-the-payment-to-the-person\">Step 3: link the payment to the person</h2>\n<p>Payments arrive from your billing provider by webhook, by a 12-month backfill when you connect, by a nightly reconcile, or through the payments API. Each one is resolved when it is written:</p>\n<ol>\n<li>the user id in the provider’s customer metadata (<code>pl_user_id</code>), matched to an <code>identify</code> call;</li>\n<li>otherwise the customer email, matched to the email you sent in identify traits;</li>\n<li>otherwise a ProductLense visitor id placed in metadata.</li>\n</ol>\n<p>The chain is <strong>payment → customer → identity → visitor → first touch</strong>, and the resolved channel, source, campaign and landing page are stored on the payment row. Resolving at write time means every revenue report is a plain query over one table. Payments that cannot be matched yet are retried nightly, so an <code>identify</code> call that arrives after the payment still credits the right channel.</p>\n<h2 id=\"step-4-compute-net-revenue\">Step 4: compute net revenue</h2>\n<p>Gross charges overstate what you keep. For every payment ProductLense stores the amount, the currency, and the fee the provider reports:</p>\n<div class=\"code-block\"><span class=\"code-lang\">text</span><pre><code class=\"language-text\">charge:                 net = amount − fee\nrefund or chargeback:   net = −(amount + fee)</code></pre></div>\n<p>Refunds subtract the fee too, because most processors keep the original fee when you refund. For merchants of record, the fee is what they keep, including the tax they remit: Paddle’s fee is grand total minus earnings, so net revenue equals your payout. Every amount is converted to USD at the European Central Bank reference rate for its day and also kept in its original currency.</p>\n<h2 id=\"step-5-subtract-ad-spend\">Step 5: subtract ad spend</h2>\n<p>Ad spend comes from manual entries or a CSV with <code>date, channel, source, medium, campaign, amount, currency</code>. It joins to revenue on channel, source, medium and campaign, the same keys the first touch carries from your UTM tags.</p>\n<div class=\"code-block\"><span class=\"code-lang\">text</span><pre><code class=\"language-text\">profit(channel, period) = Σ net revenue of payments attributed to channel and paid in period\n                          − Σ ad spend for channel in period</code></pre></div>\n<p>Then three ratios:</p>\n<div class=\"code-block\"><span class=\"code-lang\">text</span><pre><code class=\"language-text\">CAC     = ad spend / new customers\nROAS    = attributed net revenue / ad spend\npayback = CAC / average net revenue per new customer in their first 30 days</code></pre></div>\n<h2 id=\"a-worked-example\">A worked example</h2>\n<p>An example month for one channel, Google Ads, with round numbers:</p>\n<div class=\"table-wrap\"><table><thead><tr><th scope=\"col\">Item</th><th scope=\"col\">Amount</th></tr></thead><tbody><tr><td>Gross revenue from customers whose first touch was Google Ads</td><td>$1,200</td></tr><tr><td>Processor fees on those payments</td><td>$40</td></tr><tr><td>Refunds</td><td>$100</td></tr><tr><td>Net revenue</td><td>$1,060</td></tr><tr><td>Google Ads spend</td><td>$500</td></tr><tr><td><strong>Profit</strong></td><td><strong>$560</strong></td></tr><tr><td>New customers</td><td>10</td></tr><tr><td>CAC</td><td>$50</td></tr><tr><td>ROAS</td><td>2.12×</td></tr></tbody></table></div>\n<p>If those ten customers paid $53 net on average in their first 30 days, payback is $50 / $53 ≈ 0.9 months: the channel earns back what each customer cost within the first month.</p>\n<p>The same table for a channel with $2,000 of spend and the same revenue would show a loss of $940, with a minus sign and a down arrow next to it. That is the row you want to see before next month’s budget, not after.</p>\n<h2 id=\"what-this-method-does-not-do\">What this method does not do</h2>\n<ul>\n<li><strong>No multi-touch in v1.</strong> First touch gets full credit. Last touch and linear models are planned as a toggle in v1.1, computed by re-resolving the same payments.</li>\n<li><strong>No view-through attribution.</strong> An ad someone saw but did not click leaves no trace on your site, so it gets no credit.</li>\n<li><strong>Spend is manual or CSV today.</strong> Google Ads and Meta Ads spend connectors are planned for v1.1.</li>\n<li><strong>Identify matters.</strong> Without it, ProductLense can only match payments by email or a visitor id in metadata, and anonymous visits on different days stay separate.</li>\n<li><strong>Refunds land in the period they happen.</strong> A refund in November for an October sale reduces November’s net revenue for that channel, which matches your cash, not your bookings.</li>\n<li><strong>Some providers hide fees.</strong> Lemon Squeezy, Chargebee, Shopify orders and Creem do not expose fees through the APIs ProductLense reads, so profit for those connections is before fees.</li>\n</ul>\n<h2 id=\"why-first-touch-is-the-default\">Why first touch is the default</h2>\n<p>For small SaaS companies, the expensive question is which channel to put more money or time into, and that is a question about where customers start. Closing channels such as email and retargeting only work on people another channel already brought. First touch is also the model that is easiest to check by hand, which matters when the number decides a budget.</p>\n<p>Profit per channel is on the Growth and Scale plans. The <a href=\"/docs/get-started/how-productlense-counts\">methodology page</a> has every formula.</p>\n",
      "date_published": "2026-10-11T00:00:00Z"
    },
    {
      "id": "https://productlense.app/blog/introducing-productlense",
      "url": "https://productlense.app/blog/introducing-productlense",
      "title": "Introducing ProductLense: see which channels make you money",
      "summary": "ProductLense is cookieless analytics that joins your traffic to your billing provider and shows revenue and profit per channel. Here is what it does and costs.",
      "content_html": "<p>ProductLense is product analytics that shows which users pay you and which channels make a profit. Today it is available at <a href=\"https://app.productlense.app\" rel=\"noopener\">app.productlense.app</a>, free up to 10k events a month.</p>\n<h2 id=\"the-problem-it-solves\">The problem it solves</h2>\n<p>If you run a small SaaS, your numbers live in two places. Google Analytics or PostHog shows visits and clicks by source. Stripe or Paddle shows money by customer. Nothing joins the two unless you build a pipeline: export payments, match customers to users, match users to visits, and keep it all in sync as refunds and renewals arrive.</p>\n<p>So the question that decides where you spend your next month of effort, which channel brings customers who pay and keep paying, usually gets answered by gut feel or by ad platform dashboards that each claim the same sale.</p>\n<p>ProductLense answers it directly.</p>\n<h2 id=\"what-productlense-does\">What ProductLense does</h2>\n<p><strong>Cookieless analytics.</strong> A script of about 1 KB records pageviews, custom events, outbound links and downloads, with no cookies and nothing stored on the visitor’s device. Visitors are counted with a hash of the site, IP address and user agent that uses a salt that changes every day, and the IP is never stored. Most sites run it without a consent banner.</p>\n<p><strong>Revenue attribution.</strong> When a visitor signs up, your code calls <code>identify</code> with your user id. ProductLense connects to your billing provider (Stripe, Paddle, Lemon Squeezy, Polar, Dodo Payments, Razorpay, Chargebee, Shopify, Gumroad or Creem) or takes payments through an API, and follows each payment back through the customer and the user id to the visitor’s first touch: source, medium, campaign, referrer and landing page. Renewals and refunds land on the same channel automatically.</p>\n<p><strong>Profit per channel.</strong> Enter ad spend or import it as CSV, and ProductLense subtracts it, along with processor fees and refunds, from attributed revenue. Each channel, source and campaign gets profit, CAC, ROAS and payback in months. This is the number we built ProductLense for. A channel that brings a lot of revenue can still lose money once spend and refunds are counted, and a quiet newsletter can be the most profitable thing you do.</p>\n<p><strong>Revenue retention by channel.</strong> Net revenue retention cohorts, month 0 to 12, split by acquisition channel. Which channels bring customers who stay and expand, and which bring customers who cancel after a month.</p>\n<p><strong>Ask.</strong> Type a question such as “which campaign made the most profit last month” and get a real table from ProductLense’s own stats engine, with a three-sentence summary whose every number is checked against the table before it is shown. The AI turns your question into a validated query; it never queries your data and never sees raw events or personal data.</p>\n<h2 id=\"what-it-costs\">What it costs</h2>\n<div class=\"table-wrap\"><table><thead><tr><th scope=\"col\">Plan</th><th scope=\"col\">Events a month</th><th scope=\"col\">USD</th><th scope=\"col\">INR</th></tr></thead><tbody><tr><td>Free</td><td>10k</td><td>$0</td><td>₹0</td></tr><tr><td>Starter</td><td>100k</td><td>$19</td><td>₹999</td></tr><tr><td>Growth</td><td>1M</td><td>$49</td><td>₹2,499</td></tr><tr><td>Scale</td><td>10M</td><td>$149</td><td>₹6,999</td></tr></tbody></table></div>\n<p>Free includes one billing connection, so revenue by channel works from day one. Starter adds funnels, visitor journeys, three sites and 12 months of history. Growth adds every billing connection, profit, cohorts and CSV import across ten sites. Scale removes the site limit and adds API keys for server-side events and payments, and priority support. Annual billing is ten times the monthly price. Team members are unlimited on every plan. INR prices apply to accounts registered in India and include UPI. See <a href=\"/pricing\">pricing</a> for the full matrix.</p>\n<h2 id=\"your-data-stays-yours\">Your data stays yours</h2>\n<p>ProductLense never sells data and never sends raw events, emails or user ids to an AI model; Ask and the daily insight see aggregates only. “Export data” in site settings downloads a zip of a site’s daily aggregates, payments and ad spend, and deleting a site or account purges its analytics data within minutes, always within 7 days; backup copies expire within 30 days.</p>\n<h2 id=\"what-productlense-does-not-do\">What ProductLense does not do</h2>\n<p>ProductLense v1 deliberately leaves out session replay, feature flags, A/B tests, surveys, mobile SDKs and data warehouse export. If you need those, PostHog and Mixpanel do them well, and ProductLense can run alongside them with the same user id. We would rather do one job completely: what visitors do, which ones pay, and which channels make a profit.</p>\n<h2 id=\"how-it-runs\">How it runs</h2>\n<p>ProductLense runs on servers in Germany: a Go API, ClickHouse for events and Postgres for accounts and payments. Raw events are kept for 90 days and hourly aggregates for the life of the site. Billing credentials are encrypted at rest.</p>\n<p>ProductLense is built in India and operated by RedAnalytica, the same business behind Reelsmith.video. Questions, bugs and feature requests go to <a href=\"mailto:support@productlense.app\">support@productlense.app</a>.</p>\n<h2 id=\"getting-started\">Getting started</h2>\n<ol>\n<li><a href=\"https://app.productlense.app/login\" rel=\"noopener\">Sign up</a> with your email.</li>\n<li>Add the script tag to your site; there are guides for <a href=\"/docs/install/nextjs\">Next.js</a>, <a href=\"/docs/install/sveltekit\">SvelteKit</a>, <a href=\"/docs/install/wordpress\">WordPress</a> and seven others.</li>\n<li>Call <code>identify</code> after signup and connect your billing provider.</li>\n<li>Add ad spend, and read your Sources table with the profit column.</li>\n</ol>\n",
      "date_published": "2026-10-11T00:00:00Z"
    },
    {
      "id": "https://productlense.app/blog/why-cookieless-analytics-is-enough-for-saas",
      "url": "https://productlense.app/blog/why-cookieless-analytics-is-enough-for-saas",
      "title": "Why cookieless analytics is enough for SaaS",
      "summary": "What cookies buy in analytics, what a SaaS loses without them, and why identify at signup covers the part that matters for revenue attribution.",
      "content_html": "<p>Cookieless analytics is enough for most SaaS products because the moment that matters for revenue, signup, is also the moment you can identify the person with your own user id. Cookies mainly help you recognise anonymous visitors across days, and for a SaaS that gap is small, measurable, and cheaper than a consent banner.</p>\n<h2 id=\"what-a-cookie-actually-buys\">What a cookie actually buys</h2>\n<p>An analytics cookie stores a random id in the visitor’s browser. On every later visit, the tool reads it back and knows it is the same browser. That buys three things:</p>\n<ol>\n<li><strong>Returning anonymous visitors.</strong> Someone who reads your blog on Monday and comes back on Thursday is one visitor, not two.</li>\n<li><strong>Multi-day anonymous journeys.</strong> A funnel from first visit to signup that spans a week can be followed before the person tells you who they are.</li>\n<li><strong>Long first-touch windows.</strong> The channel that first brought someone weeks ago is still known when they sign up.</li>\n</ol>\n<p>It costs you a consent banner in the EU and UK, and with it every visitor who clicks “reject” or ignores the banner. It also costs a script that privacy-focused browsers and blockers treat with suspicion.</p>\n<h2 id=\"how-cookieless-counting-works\">How cookieless counting works</h2>\n<p>ProductLense counts a visitor with a hash:</p>\n<div class=\"code-block\"><span class=\"code-lang\">text</span><pre><code class=\"language-text\">visitor_id = sha256(site_id | ip | user_agent | daily_salt), first 16 hex characters</code></pre></div>\n<p>The salt is derived from a server secret and the date, and changes every 24 hours. The IP address is used for the hash and a country lookup and then discarded. Nothing is written to the browser: no cookie, no local storage.</p>\n<p>The same browser on the same day produces the same id, so sessions, bounce rate, same-day journeys and first touch for same-day signups all work. The same browser tomorrow produces a different id that cannot be linked back, because yesterday’s salt is gone.</p>\n<h2 id=\"what-you-lose\">What you lose</h2>\n<p>Being plain about the trade:</p>\n<ul>\n<li><strong>Returning anonymous visitors are counted again on each new day.</strong> Unique visitor counts over a month are higher than a cookie-based tool would report for the same traffic.</li>\n<li><strong>Anonymous journeys longer than a day break.</strong> If someone reads a comparison post on Monday, comes back from a Google search on Wednesday and signs up, the first touch ProductLense records for the signup visitor is Wednesday’s search, not Monday’s post.</li>\n<li><strong>Long consideration cycles are harder to read.</strong> If your product is bought after weeks of anonymous research, a cookie captures more of that path.</li>\n</ul>\n<h2 id=\"why-it-is-enough-for-saas\">Why it is enough for SaaS</h2>\n<p>Three reasons.</p>\n<p><strong>Signup is the identity moment.</strong> A SaaS gets a user id at signup. From then on, <code>identify</code> links every visit and every payment to that user, across days and devices, without cookies. The part of the journey that decides revenue attribution, from signup to first payment to renewals and refunds, is fully joined.</p>\n<p><strong>Most signups happen early.</strong> For self-serve SaaS, a large share of signups happen in the visitor’s first session or first day, and that is fully covered. You can check this for your own product: ProductLense’s visitor journeys show how many signups had earlier visits on the same day.</p>\n<p><strong>Complete counts beat long memory.</strong> With a banner, the visitors who decline are simply missing, and they are not a random sample. A cookieless tool counts everyone, every day. For comparing channels, complete counts with a one-day memory are usually more accurate than a long memory over a filtered sample.</p>\n<h2 id=\"what-you-gain\">What you gain</h2>\n<ul>\n<li><strong>No consent banner in most setups</strong>, because nothing is stored on the device and no IP is kept. Whether your site needs a banner still depends on your other scripts, such as ad pixels or chat widgets.</li>\n<li><strong>A 1 KB script</strong> with a CI-enforced budget, loaded with <code>defer</code>, so it does not slow down pages.</li>\n<li><strong>Less personal data to hold.</strong> No IPs, day-scoped pseudonymous ids, raw events deleted after 90 days. Personal data appears only when you choose to send a user id or email with <code>identify</code>.</li>\n<li><strong>Friendlier to privacy browsers.</strong> There is no third-party cookie to block, so data from privacy-conscious visitors is less likely to be missing.</li>\n</ul>\n<h2 id=\"cookieless-is-not-no-personal-data\">Cookieless is not “no personal data”</h2>\n<p>Under GDPR, an IP address is personal data even if you only hold it for milliseconds, and a hashed id can still count as pseudonymous data. Cookieless analytics reduces what is processed and stored, but it is still processing. That is why ProductLense acts as a processor under a <a href=\"/legal/dpa\">DPA</a>, lists its subprocessors on request, keeps data in Germany, and documents every field in the <a href=\"/docs/privacy/what-productlense-collects\">privacy docs</a>. The same applies under India’s DPDP Act, where your company is the data fiduciary and ProductLense is your processor.</p>\n<h2 id=\"how-to-get-the-most-from-it\">How to get the most from it</h2>\n<ol>\n<li>Call <code>identify</code> at signup and login, with the same user id you store on your billing provider’s customers as <code>pl_user_id</code>.</li>\n<li>Tag every link you control with UTM parameters, so a newsletter click is not counted as direct traffic.</li>\n<li>Look at the Sources table by first-touch channel with revenue and profit, not just visitors.</li>\n<li>If your product has long anonymous research cycles, compare signup counts by day-of-first-visit in visitor journeys, and decide with your own data whether the one-day window loses anything you care about.</li>\n</ol>\n<p>For a typical SaaS, it does not. Read <a href=\"/features/revenue-attribution\">how ProductLense attributes revenue</a> or <a href=\"/docs/install/script-tag\">install the script</a> and check with your own traffic.</p>\n",
      "date_published": "2026-10-11T00:00:00Z"
    },
    {
      "id": "https://productlense.app/changelog#2026-10-11-first-public-release",
      "url": "https://productlense.app/changelog#2026-10-11-first-public-release",
      "title": "Changelog 0.1.0: First public release",
      "summary": "ProductLense 0.1.0: cookieless tracking, ten billing connectors plus a payments API, profit per channel, cohorts by channel, Ask and the daily insight.",
      "content_html": "<p>The first public version of ProductLense, at app.productlense.app.</p>\n<h3 id=\"added\">Added</h3>\n<ul>\n<li><strong>Tracking script</strong> (<code>a.js</code>, budget 1,200 bytes gzipped): pageviews including SPA route changes, custom events with props, <code>identify</code>, outbound link clicks and file downloads. No cookies, no local storage.</li>\n<li><strong>Dashboard pages</strong>: Overview, Sources, Pages, Geo and tech, Goals and funnels, Visitors with journeys, Revenue, Profit, Live, and Settings.</li>\n<li><strong>Billing connectors</strong> for Stripe, Paddle, Lemon Squeezy, Polar, Dodo Payments, Razorpay, Chargebee, Shopify, Gumroad and Creem, each with a 12-month backfill, signed webhooks and a nightly reconcile.</li>\n<li><strong>Payments API</strong> at <code>POST /api/v1/payments</code> for any other source, and server-side events at <code>POST /api/v1/events</code>.</li>\n<li><strong>First-touch attribution</strong> resolved when each payment is written, with a nightly re-resolve for late <code>identify</code> calls.</li>\n<li><strong>Ad spend</strong> by manual entry and CSV import.</li>\n<li><strong>Profit per channel</strong> with CAC, ROAS and payback months.</li>\n<li><strong>Net revenue retention cohorts by channel</strong>, months 0 to 12.</li>\n<li><strong>Ask</strong> and the <strong>daily insight</strong>, with every number in a summary checked against the query result.</li>\n<li><strong>Plans</strong> Free, Starter, Growth and Scale in USD and INR, billed through Dodo Payments.</li>\n</ul>\n",
      "date_published": "2026-10-11T00:00:00Z"
    }
  ]
}