Skip to content

Net revenue retention: keep and grow, one number

Net revenue retention reads what your Customers of 90 days ago pay today, beside logo churn and the gross line.

Net revenue retention is the ARR your Customers of 90 days ago pay today, upgrades and downgrades included. It reads 85 % on leaseline.app.

The number

Keep and grow, one number
You, to your agent

How much of last quarter’s ARR did our Customers keep and grow?

curl -X POST https://api.sb/leaseline.app/metrics/query \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"metric":"net-revenue-retention","shape":"retention","window":"90d"}'

curl -X POST https://api.sb/leaseline.app/metrics/query \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"metric":"logo-churn","shape":"trend","window":"90d"}'

# Not live yet (gaps #4)
curl -X POST https://api.sb/leaseline.app/metrics/query \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"metric":"gross-revenue-retention","shape":"retention","window":"90d"}'
import { $ } from 'startup-builder'

const nrr = await $.Metric.query({ metric: 'net-revenue-retention', shape: 'retention', window: '90d' })   // the Customers earning on Jul 21, 90 days on
const churn = await $.Metric.query({ metric: 'logo-churn', shape: 'trend', window: '90d' })   // how many of them left
return await $.Metric.query({ metric: 'gross-revenue-retention', shape: 'retention', window: '90d' })   // the same, before upgrades and win-backs · Not live yet, gaps #4
npx startup-builder do Metric.query --json '{"metric":"net-revenue-retention","shape":"retention","window":"90d"}'
npx startup-builder do Metric.query --json '{"metric":"logo-churn","shape":"trend","window":"90d"}'
npx startup-builder do Metric.query --json '{"metric":"gross-revenue-retention","shape":"retention","window":"90d"}'   # Not live yet (gaps #4)
{
  "name": "do",
  "arguments": {
    "code": "const nrr = await $.Metric.query({ metric: 'net-revenue-retention', shape: 'retention', window: '90d' })   // the Customers earning on Jul 21, 90 days on\nconst churn = await $.Metric.query({ metric: 'logo-churn', shape: 'trend', window: '90d' })   // how many of them left\nreturn await $.Metric.query({ metric: 'gross-revenue-retention', shape: 'retention', window: '90d' })   // the same, before upgrades and win-backs · Not live yet, gaps #4"
  }
}
Response200application/json
{
  "cohort": "2026-07-21",
  "customers": 13,
  "startArr": 4511,
  "moves": {
    "reactivatedArr": 0,
    "expansionArr": 694,
    "contractionArr": 0,
    "churnedArr": -1388,
    "oneTimeRolloffArr": 0
  },
  "endArr": 3817,
  "rows": [
    {
  …
sample · 1 Not live yet

85 % net revenue retention · ■■□□ Early sign · n 13 Customers · 90 days

$.Metric.query({ metric: 'net-revenue-retention', shape: 'retention', window: '90d' }) · sample

The Customers earning on Jul 21ARR thenARR 90 days onKept and grown
13$4,511 ARR$3,817 ARR85 %

The hub defines it: net-revenue-retention. The CSM’s number is this reading on its own book; Head of customers reads it for the Startup.

Beside it

Reading90 daysWhat it counts
logo-churn31 %4 of 13 Customers left
The gross line, gross-revenue-retention (Not live yet, gaps #4)69 %the same ARR before upgrades and win-backs: only Downgrades, Lost Customers and One-time sales ending

All three are readings with no parent: none of them redefines a number in the tree.

Its leaves

Move90 daysIts page
Won back$0 ARRWon back
Upgrades+$694 ARRUpgrades
Downgrades$0 ARRDowngrades
Lost Customers−$1,388 ARRLost Customers
One-time sales ending$0 ARROne-time sales ending

Compiled, not written: edit its page, then regenerate. Every step runs from a sentence or a line of code. In your words: the CRM words you know, and where each lives here.

Contents