4 min read

How to Set Up Alerts for LTV in Stripe

Stripe doesn't calculate LTV for you—you have to pull subscription and payment data yourself. But that's actually good news: you can set up webhooks to trigger alerts whenever a customer's lifetime value hits a milestone or changes significantly. This guide shows you how.

Create a Webhook Endpoint to Listen for Subscription Events

Webhooks let Stripe notify your app whenever something relevant happens (subscription created, updated, payment received). You'll listen for these events and calculate LTV on the fly.

Create an endpoint to receive webhook events

Set up an HTTP endpoint that Stripe can POST to. You'll verify the signature to make sure the event is legit, then process it. Use your Secret Key from the Stripe Dashboard to verify signatures.

javascript
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const app = express();

const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;

app.post('/webhooks/stripe', express.raw({type: 'application/json'}), async (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  try {
    event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
  } catch (err) {
    console.error(`Webhook signature verification failed: ${err.message}`);
    return res.sendStatus(400);
  }

  if (['customer.subscription.updated', 'invoice.payment_succeeded'].includes(event.type)) {
    const customerId = event.data.object.customer;
    const ltv = await calculateCustomerLTV(customerId);
    await checkLTVAlert(customerId, ltv);
  }

  res.json({received: true});
});

app.listen(3000, () => console.log('Webhook server running'));
The `constructEvent()` call validates the webhook signature using your signing secret. This proves Stripe actually sent it.

Register your endpoint in the Stripe Dashboard

Go to Developers > Webhooks in the Stripe Dashboard. Click Add endpoint and paste your webhook URL. Select customer.subscription.updated, invoice.payment_succeeded, and charge.refunded to catch all LTV changes.

Query Stripe to Calculate Each Customer's Total Revenue

Once you know which customer changed, fetch their subscription and invoice history to sum up total lifetime revenue.

Fetch all paid invoices for the customer

Use stripe.invoices.list() to get all invoices for the customer. Sum the amount_paid field to get total revenue. Filter by status: 'paid' to exclude drafts and failed charges.

javascript
async function calculateCustomerLTV(customerId) {
  const invoices = await stripe.invoices.list({
    customer: customerId,
    limit: 100,
    status: 'paid'
  });

  let totalRevenue = 0;
  invoices.data.forEach(invoice => {
    totalRevenue += invoice.amount_paid;
  });

  return totalRevenue / 100; // Convert from cents to dollars
}
Stripe returns amounts in cents, so divide by 100 for dollars. Use `limit: 100` and loop through all pages if the customer has more invoices.

Handle refunds separately

A refunded charge lowers LTV but status: 'paid' only includes successfully collected invoices. Track charge.refunded events in your webhook to subtract refunded amounts from your cached LTV. Watch out: If a customer disputes a charge, the refund may come weeks later—your alert logic needs to account for that lag.

Tip: Cache LTV in Redis or your database. Querying Stripe every webhook is slow and burns rate limits. Update the cache incrementally when events arrive instead of recalculating from scratch.

Create Alert Logic Based on LTV Thresholds

Now that you have the LTV, define when to alert. Common triggers: LTV crosses a milestone, LTV drops suddenly, or a high-value customer arrives.

Set up alert conditions in your webhook handler

After calculating LTV, compare it to thresholds. Send alerts via email, Slack, or your internal system. Track both absolute thresholds (e.g., over $5000) and relative changes (e.g., dropped 20%) to catch both high-value wins and churn signals.

javascript
async function checkLTVAlert(customerId, ltv) {
  const ALERT_THRESHOLD = 5000;
  const CHURN_ALERT_THRESHOLD = 0.8;

  const previousLTV = await redis.get(`ltv_${customerId}`) || 0;
  const percentChange = previousLTV > 0 ? (ltv - previousLTV) / previousLTV : 0;

  if (ltv > ALERT_THRESHOLD) {
    await sendAlert(`High-value customer: ${customerId} reached $${ltv.toFixed(2)} LTV`);
  }

  if (percentChange < -CHURN_ALERT_THRESHOLD && previousLTV > 0) {
    await sendAlert(`Churn warning: ${customerId} LTV dropped from $${previousLTV} to $${ltv}`);
  }

  await redis.set(`ltv_${customerId}`, ltv);
}

async function sendAlert(message) {
  await fetch('https://hooks.slack.com/services/YOUR/WEBHOOK/URL', {
    method: 'POST',
    body: JSON.stringify({ text: message })
  });
}
Compare current LTV to cached previous value. Send two types of alerts: absolute thresholds (customer is valuable) and relative changes (customer is churning).

Handle pagination for high-invoice customers

Stripe paginates invoice lists in batches of 100. If a customer has hundreds of invoices, you need to loop through all pages using the starting_after parameter to get the complete LTV, not just the first 100.

Tip: Store LTV snapshots in your database with timestamps. This lets you detect month-over-month trends and churn velocity, not just one-time spikes.

Common Pitfalls

  • Forgetting pagination—customers with many invoices will have incomplete LTV if you only fetch the first batch of 100. Loop through all pages.
  • Counting refunded charges as revenue—always filter for status: 'paid' to exclude refunds and drafts.
  • Querying Stripe on every webhook instead of caching—you'll hit rate limits and slow down alerts. Cache LTV locally and update incrementally.
  • Ignoring the time lag on refunds—a refund event may come weeks after the original charge. Your alert logic needs to account for delayed adjustments to LTV.

Wrapping Up

You now have real-time LTV alerts by combining Stripe webhooks with invoice queries and your own alert logic. Store the results in your database so you can track trends over time and find your most valuable customers before they churn. If you want to track this automatically across tools, Product Analyst can help.

Track these metrics automatically

Product Analyst connects to your stack and surfaces the insights that matter.

Try Product Analyst — Free