SaaS and technology · Recurring revenue

MRR and its movements, with NRR and GRR

How much MRR came in, expanded, contracted, churned and returned each month, by customer, matching finance and without rewriting the past.

Tested with an agentOct 2026

UsageWidely used
EffortHigh
ForSaaS companies that need to explain month-over-month MRR changes to leadership and the board.
Source

Billing subscriptions, items, prices and invoices

Rule

End-of-month state compared with the previous month, by customer

Outcome

MRR movements, NRR and GRR

Activation

Close, board and AI agent

What it solves

  • The report's MRR matches billing and finance at close.
  • Every real of change has a category: new, expansion, contraction, churn or reactivation.
  • A closed month's number doesn't change after it has been reported.

Business questions it answers

Ask your agent these questions once the case is live.

How much MRR did we lose to churn in August, and how much was just contraction?
What were NRR and GRR over the last 12 months?
How much of the quarter's new revenue came from the base and how much from new customers?
Which product concentrates revenue churn?
Which customers started paying again after canceling?

What you need

  • Billing connected to Nekt (subscriptions, items, prices and invoices)
  • A daily history of subscription state, stored starting now
  • The rule agreed with finance for annual plans, discounts, usage, delinquency and currency

What you get

  • A customer x month table with starting MRR, ending MRR and the classified movement
  • NRR, GRR and churn by month, quarter and trailing 12 months
  • An MRR waterfall for the close and for the board
  • An AI agent that answers revenue questions with the right rule

Where to activate

Close and board

The month's and quarter's MRR waterfall with NRR and GRR, read from the frozen table.

AI agent via MCP

Answers about churn, expansion and NRR with the customer-month grain and status rule already applied.

Alerts

A daily list of customers who contracted or left since the end of the previous month, so CS can act before the close.

Ready to build?

The rules are settled, but the history has to exist. If billing doesn't keep versions, start the daily snapshot today and rebuild the past from invoices. The detailed view has the business rules, the pitfalls and the acceptance test. Or send everything straight to your agent with the Nekt MCP.

The problem

Billing shows the subscription as it is now: whoever canceled shows up with zero MRR and whoever changed plans shows up with only the new plan. Summing the change log counts movement that came and went in the same month, and comparing subscriptions instead of customers calls a plan change churn. The result is a different NRR in every spreadsheet and a closed month that changes after it has been reported.

How it is usually done, and the rule

Each row is a mistake that already happened in practice.

The common wayThe rule
01
Summing the month's subscription change event log.
A movement is the difference between two end-of-month snapshots

Compute each customer's MRR on the last day of the month and compare it with the last day of the previous month. Each customer-month falls into a single movement. The raw change log counts back-and-forth changes within the month and, in one real base, inflated movement by about 18%. The month's snapshot uses the last day of the month for every segment, never the last day each segment had a row.

02
Counting each canceled subscription as a churn.
The grain is the customer, not the subscription

Churn is a customer who had MRR at the end of the previous month and ends the month with no active subscription. A plan change, renewal or subscription swap for the same customer is expansion, contraction or nothing. Migrating the same customer from one product to another can still count as churn on the source and new on the destination in the per-product view, but must be flagged separately so it doesn't appear as logo loss. Economic-group consolidation follows the rule the workspace already has: if the rule is not to consolidate, each billing account is a customer and only the groups listed in the exceptions are merged.

03
Using unit_amount x quantity for every item and letting tiered or usage prices count as zero without noticing.
MRR is monthly-normalized and no active item is left at zero

Annual, semiannual, quarterly or non-standard cycle plans go in divided by the months in the cycle (in Stripe, price.unit_amount x quantity divided by the months of price.recurring.interval x interval_count). Prices with billing_scheme = tiered or usage_type = metered have no unit_amount, and the formula returns zero with no error: value that item by the total of the lines on its latest paid invoice (a tiered item produces several lines in the same period, sum them all) or sum the tiers for the contracted quantity. Before moving on, list the items on active subscriptions that ended up with zero MRR: the list must be empty or every case explained. When the source is the invoice, allocate the recurring amount across the accrual months it covers.

04
Summing the whole invoice, with implementation, one-off credits and variable usage.
Only recurring revenue goes into MRR

Implementation, setup, one-off credit purchases (one_time price), prorations, one-off services, marketplace fees and partner revenue stay out. Usage-based billing (metered price) only counts in MRR up to the contractual minimum; variable usage above it goes to its own usage revenue column, otherwise every swing in consumption becomes expansion or contraction. A recurring discount reduces MRR; declare when a temporary discount starts to count (some only consider open-ended discounts or those lasting more than 3 months).

05
Summing items from every subscription, or removing past_due and adding it back the next month.
Status decides who is in the snapshot

Active subscriptions go in. Trialing is not MRR. Canceled and incomplete_expired go out, and the filter is mandatory because a canceled subscription still has items and price filled in. Past_due is a business decision: the safest is to keep it in MRR flagged as at risk until it becomes canceled or unpaid. Removing past_due from MRR creates churn and reactivation that never happened when the invoice gets paid. Unpaid is also a declared decision: state whether it leaves MRR and check it against the finance report's criterion, which often keeps unpaid, or the reconciliation won't close. A freeze shows up in Stripe as status paused or pause_collection filled in on the subscription: classify it as a freeze, not as churn.

06
Reading the canceled subscription's current MRR and finding zero.
Churn uses the last MRR before leaving

On cancellation, the customer's current MRR goes to zero. The amount lost is the customer's MRR at the end of the previous month, or the last known value before canceled_at. Zero MRR never means a free customer: it means a customer who left.

07
Counting the returning customer as a new customer.
Reactivation is someone who paid before and returns

A customer with zero MRR at the end of the previous month and more than zero now is new if they never paid and a reactivation if they had MRR in some earlier month. Reactivation doesn't go into the cohort's NRR, because the customer wasn't in the starting base.

08
Computing past months' MRR with today's subscriptions table.
Keep the history yourself

Billing exposes the current state, and Stripe's events API only keeps the last few days. A change in quantity or plan on the same subscription disappears. Write a daily snapshot of subscriptions and items, or a history with valid from and valid to, from day one. For the months before the history starts, rebuild from paid invoice lines with a recurring price and no proration, counting each line in the month whose last day falls inside its service period (period.start to period.end). The first month with a snapshot compares two different sources: flag it as the seam month and keep its movement out of the board deck.

09
Recomputing every month on every run.
A closed month is frozen

After the close, write the month's result as a provisional version and allow a single correction, which becomes final. Later retroactive corrections go in as an adjustment in the month they were discovered, with the reference month recorded, never rewriting the number already reported.

10
Summing BRL and USD in the same column.
Never sum currencies

Convert everything to the reporting currency at a declared rate and use the same fixed rate across the whole movement series: with a monthly rate, a foreign-currency customer who didn't change plans shows up as expansion or contraction. If you need the view at realized exchange rates, show the FX effect on a separate line. Check whether the converted column comes through null for foreign currency before summing.

How each customer-month is classified

Comparing the customer's MRR at the end of the previous month (start) with the end of the month (end).

start = 0, end > 0, never paidNew
start = 0, end > 0, paid beforeReactivation
start > 0, end > startExpansionend - start
start > 0, 0 < end < startContractionstart - end
start > 0, end = 0Churnstart
start > 0, end = startStable
subscription with status paused or pause_collection filled inFreeze, separate from churn
start = 0, end = 0Outside the base, no row and no category

Definitions

Customer MRR
sum, on the last day of the month, of the monthly-normalized amount of recurring items on the customer's active subscriptions, net of recurring discounts; a tiered or usage item is worth what its latest paid invoice charged, up to the contractual minimum, and variable usage goes in its own column
Starting MRR
customers' MRR on the last day of the previous month
New MRR
end-of-month MRR of customers who had never paid
Expansion
end - start, only where end > start > 0, by customer and month
Contraction
start - end, only where start > end > 0, by customer and month
MRR churn
starting MRR of customers who end the month with zero MRR; logo churn is the count of those customers
Reactivation
end-of-month MRR of customers who had zero MRR at the start and paid in some earlier month
GRR
(starting MRR - contraction - churn) / starting MRR, only with customers in the starting base; never exceeds 100%
NRR
(starting MRR + expansion - contraction - churn) / starting MRR, only with customers in the starting base, with no new or reactivation; over 12 months, compare the same group of customers at the start and end of the window

Pitfalls that already happened

Tiered prices counted as zero

Items with tiered or metered prices have no unit_amount, and the price x quantity formula counted them as zero. A few of those items accounted for a meaningful share of MRR, and the total came out several points below finance with no error in the query.

Value those items from the latest paid invoice and list active items with zero MRR before closing the month.

Bulk cancellation

Subscriptions with no payment for months were canceled all at once and all the churn landed in a single month.

Close the exit at the earlier of the cancellation date and the last payment plus one cycle.

Phantom reactivations and orphan churn

A model based on the product screen, not on billing, created activations and reactivations that were never charged and churn from customers with no billed MRR.

Rebuild the start and end state from billing only, with valid from and valid to.

Rewritten cycle date

When a subscription recovers from a failed charge, billing pushes the cycle start date forward, and long-standing customers looked new.

Anchor the customer's start on the first confirmed payment, never on the cycle date.

Customer counted several times

The same group with several CNPJs (company tax IDs) or several paying sub-accounts showed up as several active customers and as several churns.

Follow the workspace's consolidation rule: if it consolidates, use the canonical customer and the CNPJ with the highest MRR as the primary one; if it doesn't, merge only the groups listed in the exceptions.

The number changed after the board meeting

The table was recomputed on every run and the MRR of closed months changed after being presented.

Freeze the closed month and record adjustments in the month they were discovered.

Validated on real data. In Nekt's own Stripe account, more than 8 in 10 canceled subscriptions still carry items with a price above zero, so summing items without filtering status puts customers who already left into MRR. And some subscriptions that went through past_due returned to active later, which is why removing past_due from MRR would create churn and reactivation that never happened.

Acceptance test

The case is done when every item passes.

Agent package

Paste into Claude, ChatGPT or any client with the Nekt MCP connected.

Full Markdown
MRR movements
- Customer MRR = monthly-normalized amount of recurring items on active subscriptions on the last day of the month, net of recurring discounts. Cycles longer than a month divided by the months in the cycle. A tiered or usage item is worth what its latest paid invoice charged.
- Outside MRR: implementation, setup, one-off credits (one_time price), prorations, one-off services, marketplace fees and partner revenue. Variable usage above the contractual minimum goes in the usage revenue column.
- Status: active goes in; trialing, canceled and incomplete_expired don't. Past_due stays in MRR flagged as at risk until it becomes canceled or unpaid. Unpaid: <in or out>. Paused or pause_collection filled in = freeze, not churn.
- Movement = the customer's MRR at month end compared with the end of the previous month. One movement per customer-month: new, reactivation, expansion, contraction, churn or stable. Zero at start and end produces no row.
- Churn is at the customer level: only when the customer ends the month with no active subscription. A plan change is not churn. Churn amount = the customer's MRR at the end of the previous month.
- Zero MRR doesn't mean a free customer. It means a customer who left.
- Reactivation = a customer with zero MRR at the start who paid before. It doesn't go into NRR.
- GRR = (starting MRR - contraction - churn) / starting MRR. NRR = (starting MRR + expansion - contraction - churn) / starting MRR. Only customers in the starting base.
- Never sum different currencies. Reporting currency: <currency>, fixed rate for the whole series: <declared rate>.
- Months before <history start> come from paid invoices by service period. <seam month> is the seam month between sources and doesn't go to the board.
- Economic groups: <consolidated or not consolidated>, per the workspace rule.
- Closed months come from the frozen table. Don't recompute or quote a closed month's number from another source.
Per-client exceptions:
- <client>: migration from product <source> to <destination> within 30 days is flagged as a migration, not as logo churn.
- <client>: economic group with several CNPJs (company tax IDs) consolidated under CNPJ <primary>.
- <client>: billed outside the billing system; add <recurring amount> to MRR from <month>.
- <client>: usage price with a contractual minimum of <amount>, which is the part that counts in MRR.

Variations

Per-product view

The same rule at the customer x product x month grain. Cross-sell shows up as expansion with a new-product flag.

Billing with no history

Rebuild the past from paid invoice lines by service period, start the daily subscription snapshot today and flag the seam month between the two sources.

MRR from the CRM

When the contract lives in the CRM, use the recurring amount from the won deal's line items, never the deal's total amount field, and check it against billing.