Back arrowAll guides

Telling expansion accounts from at-risk accounts in your Stripe feedback

Pixel art of glowing analytics bars diverging up and down on a dark navy background
Talton Figgins•••6 min read

Look at what each account's subscription has done in Stripe over the last billing cycle or two, not what plan they're on today. An account that just upgraded, added seats, or paid cleanly on the first attempt is trending toward more revenue. An account sitting in past_due status, mid a failed-payment retry, or with a downgrade already scheduled is trending the other way. Two accounts on the same plan, asking for the same feature, can be pointed in opposite directions, and Stripe's subscription and invoice events are exactly where that direction shows up first.

Plan tier alone won't tell you this. A $2,000/month account and a $2,000/month account look identical on a feature board until you check what each one's billing has been doing lately. Here's where that trajectory lives in Stripe, how to read it into a triage decision, and the point past which checking two dashboards by hand for every request stops being realistic.

Where the two signals live in Stripe

Stripe doesn't score accounts as "expanding" or "at risk." It records events, and the two that matter here are the same two categories Stripe already uses to calculate MRR growth on its own billing analytics page, expansion and contraction. The dashboard's downloadable Customer MRR changes report is described in Stripe's own docs as "a log of every MRR change for each customer, including new subscribers, upgrades, downgrades, reactivations, and churn," which is close to a ready-made feed for this if you're willing to pull it and join it by hand.

For a live signal instead of a monthly export, two webhook events carry the same information as it happens:

  • customer.subscription.updated fires, per Stripe's event types reference, "whenever a subscription changes (e.g., switching from one plan to another, or changing the status from trial to active)." A price change on the subscription's items, seats going from 10 to 25, or a tier moving from Growth to Enterprise, comes through here. Compare the new price against what you have on file for that account; a jump is expansion, a drop is contraction.
  • invoice.payment_failed fires, per the same reference, "whenever an invoice payment attempt fails, due to either a declined payment, including soft decline, or to the lack of a stored payment method." The invoice's attempt_count tells you how many tries have already happened.

A failed payment doesn't mean the account is gone. Stripe's Smart Retries will keep attempting the charge on its own schedule, "8 tries within 2 weeks" by default, and plenty of failures are an expired card, not an unhappy customer. What the event does mean is that the account's subscription status has moved to past_due, which Stripe's subscription lifecycle docs define as "payment on the latest finalized invoice either failed or wasn't attempted." That status, sitting next to a feature request from the same account, is worth a second look before the request gets ranked the same as everyone else's.

“we need SSO before rollout”
Slack logo“any update on single sign-on?”
Zendesk logo“SSO came up twice on this call”
↓ classified + deduped into
SSO requests
8 accounts asking · quotes kept
filesLinear logoLinear issue, quotes attached
writesNotion logoinsight report in Notion
answersthe account team
Same feature, same plan tier, opposite trajectory. One account's subscription just expanded; the other's is sitting in a payment retry. Nothing about the request itself carries that difference.

The manual check, and where it breaks down

That comparison is repeatable as a five-minute check for any account that files a request:

  1. Open the account's subscription in Stripe and check the current status: active, past_due, unpaid, or a scheduled downgrade waiting to apply.
  2. Check the last 60 days of invoices for the account. A recent invoice.payment_failed with attempt_count climbing, or a subscription sitting in past_due, is the at-risk signal.
  3. Check for a recent customer.subscription.updated where the new price is higher than the old one, seats added or a tier upgrade, which is the expansion signal.
  4. Weigh the request accordingly. An expansion account's request is a hint about where the product needs to keep up with how the account is already growing. An at-risk account's request is sometimes a genuine blocker worth fixing fast, and sometimes noise from someone about to leave regardless; either way it shouldn't get weighted the same as a healthy account's ask by default.
  5. Route the account, not just the request, to whoever owns that relationship, growth or success, so the billing signal doesn't dead-end on the feedback board.

That routine holds up for a handful of requests a week, but it breaks down for reasons that have nothing to do with the data being hard to read:

  • The check depends on someone remembering to do it. Nothing on the feedback board prompts it. Whoever triages the board that day files both requests as identical and never opens Stripe at all.
  • Trajectory goes stale immediately. An account flagged healthy today can have a failed payment tomorrow, and nothing re-checks it unless a person does, by hand, again.
  • The billing signal and the feedback board live in separate systems. Stripe has the subscription status; the feedback tool has the request; nothing ties the two together automatically, so the join happens in someone's head or not at all.

Keeping the trajectory attached to the request

Once a team is triaging more than a handful of requests a week, or feedback is arriving from more accounts than any one person can keep straight in their head, steps 1 through 3 above stop being something that happens every time and start being something that happens when someone remembers.

Automating that is one of the things Modem is built to do. Modem's Stripe integration keeps each company's plan and subscription status current on its record, alongside every feature request, support thread, and call note tied to that same account, refreshed as billing events happen rather than checked one dashboard at a time. Its Stripe automations already cover subscriptions starting, canceling, trials ending, and failed payments, so an automation you set up can flag a request from an account with a failed payment in that same window, next to whatever else is true of the account, instead of requiring someone to open Stripe first. I should say plainly that I work on Modem, so read that as an interested recommendation, not neutral advice; a manual check that's already covering your current volume fine doesn't need replacing just because a tool for it exists. Keeping billing status attached to the request instead of filed separately is the same context graph approach behind weighing requests by revenue in how to weight feature requests by Stripe ARR instead of vote count.

Whether or not automating it makes sense yet, the immediate test doesn't change. Pull the last five feature requests on your board and check each account's Stripe status, active and recently upgraded, or past_due with a retry pending. That alone splits requests you'd otherwise have weighted identically into two different conversations, which is the whole point of looking in the first place.