Revenue: where ARR moved, and which Deals close next
From the first reply to ARR.
Where ARR moved this week, and which Deals close next. The six moves add up to the change, and your SDR, AE and CSM each get one next step.
- $2,082New Customers
- $0Won back
- $347Upgrades
- $0Downgrades
- $1,041Lost Customers
- $0One-time sales ending
- $0Not yet traced
- +$1,388ARR added this week
sample · leaseline.app · week of 2026-10-19Where did ARR move this week, and which Deals close next?
# 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":"net-new-arr","shape":"trend","window":"56d"}'import { $ } from 'startup-builder'
return await $.Metric.query({ metric: 'net-new-arr', shape: 'trend', window: '56d' }) // eight Mondays, each split into the six moves · Not live yet, gaps #4
npx startup-builder do Metric.query --json '{"metric":"net-new-arr","shape":"trend","window":"56d"}' # Not live yet (gaps #4){
"name": "do",
"arguments": {
"code": "return await $.Metric.query({ metric: 'net-new-arr', shape: 'trend', window: '56d' }) // eight Mondays, each split into the six moves · Not live yet, gaps #4"
}
}application/json{
"metric": {
"$id": "https: //api.sb/ leaseline.app/ metrics/ net-new-arr",
"$type": "Metric",
"name": "Net new ARR"
},
"asOf": "2026-10-19T00: 00: 00Z",
"rows": [
{
"week": "2026-08-31",
"value": 694
},
{
"week": "2026-09-07",
…
sample · 1 Not live yet+$1,
$.Metric.query({ metric: · sample
Monday review
| Move | This week | Worked by |
|---|---|---|
| New Customers | +$2, | You, as head of sales |
| Won back | $0 ARR | Head of customers |
| Upgrades | +$347 ARR | CSM |
| Downgrades | $0 ARR | CSM |
| Lost Customers | −$1, | CSM |
| One-time sales ending | $0 ARR | Head of customers |
| Not yet traced | $0 ARR | You, as head of sales |
| ARR added this week | +$1, | You, as head of sales |
Biggest mover: Lost Customers, −$1,: 3 Customers, 2 of them at risk a week earlier. Against your number, the week is $412 ARR short of the +$1, a week it needs.
| Title | Hours this week | What it does | Expected | The call that starts it |
|---|---|---|---|---|
| CSM | 2 h | the 4 Customers now at risk, Juniper Homes first: it renews Nov 3 | Lost Customers better than −$400 ARR by Oct 26 | POST /{namespace}/renewals/open |
| AE | 1 h | Northgate Rentals: approve 30 % off the $2,400 plan on a two-year term | New Customers +$1, once Northgate Rentals is won | POST /{namespace}/approvals/{approval}/approve |
| SDR | 1 h | approve the first wave to the 18 Leads lead reactivation re-opened | Deals and trials opened +4 by Oct 26 | POST /{namespace}/approvals/{approval}/approve |
Where is the number this week, and what do we change? Start the next step for our SDR, AE and CSM in try mode.
curl -X POST "https://api.sb/leaseline.app/renewals/open?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"subscription":"subscription_7"}'
curl -X POST "https://api.sb/leaseline.app/approvals/approval_3/approve?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"note":"Two-year term"}'
curl -X POST "https://api.sb/leaseline.app/approvals/approval_5/approve?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# 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":"net-new-arr","shape":"trend","window":"7d","breakdown":"node"}'import { $ } from 'startup-builder'
const { renewal } = await $.Renewal.open({ subscription: 'subscription_7' }, { mode: 'try' }) // CSM · 2 h · the 4 Customers now at risk, Juniper Homes first (renews Nov 3)
const discount = await $.Approval.approve({ approval: 'approval_3', note: 'Two-year term' }, { mode: 'try' }) // AE · 1 h · 30 % off for Northgate Rentals on a two-year term
const wave = await $.Approval.approve({ approval: 'approval_5' }, { mode: 'try' }) // SDR · 1 h · the first wave to the 18 re-opened Leads
return await $.Metric.query({ metric: 'net-new-arr', shape: 'trend', window: '7d', breakdown: 'node' }) // the week, by move · Not live yet, gaps #4
npx startup-builder do Renewal.open --json '{"subscription":"subscription_7"}' --try
npx startup-builder do Approval.approve --json '{"approval":"approval_3","note":"Two-year term"}' --try
npx startup-builder do Approval.approve --json '{"approval":"approval_5"}' --try
npx startup-builder do Metric.query --json '{"metric":"net-new-arr","shape":"trend","window":"7d","breakdown":"node"}' # Not live yet (gaps #4){
"name": "do",
"arguments": {
"code": "const { renewal } = await $.Renewal.open({ subscription: 'subscription_7' }, { mode: 'try' }) // CSM · 2 h · the 4 Customers now at risk, Juniper Homes first (renews Nov 3)\nconst discount = await $.Approval.approve({ approval: 'approval_3', note: 'Two-year term' }, { mode: 'try' }) // AE · 1 h · 30 % off for Northgate Rentals on a two-year term\nconst wave = await $.Approval.approve({ approval: 'approval_5' }, { mode: 'try' }) // SDR · 1 h · the first wave to the 18 re-opened Leads\nreturn await $.Metric.query({ metric: 'net-new-arr', shape: 'trend', window: '7d', breakdown: 'node' }) // the week, by move · Not live yet, gaps #4",
"mode": "try"
}
}application/json{
"week": "2026-10-19",
"moves": {
"newArr": 2082,
"reactivatedArr": 0,
"expansionArr": 347,
"contractionArr": 0,
"churnedArr": -1041,
"oneTimeRolloffArr": 0,
"unexplained": 0,
"netNewArr": 1388,
"arr": 9021
},
"target": {
…
sample · 1 Not live yetThe hub runs this review as the Workflow monday-role-review.
Deal review
| Lost reason | Deals | ARR lost | The Play that answers it |
|---|---|---|---|
price | 5 | −$5, | Risk-reversal offers |
no-decision | 3 | −$3, | Buying committee |
missing-capability | 2 | −$694 ARR | Lead reactivation |
timing | 2 | −$694 ARR | Lead nurture |
competitor | 1 | −$347 ARR | Switch kit |
no-fit | 1 | −$347 ARR | Positioning tournament |
The typed reason is Not live yet, gaps #2; today POST /{namespace}/deals/{deal}/lose keeps the free text, and the window reads it from the Deal.lost Events.
Why are we losing deals, and what do we change?
curl "https://api.sb/leaseline.app/events?where=name%20%3D%20%22Deal.lost%22%20and%20at%20%3E%3D%20%222026-07-21%22&limit=14" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# Not live yet (gaps #2)
curl "https://api.sb/leaseline.app/deals?where=status%20%3D%20%22Lost%22&facets=lostReason" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"import { $ } from 'startup-builder'
const { events } = await $.Event.list({ where: 'name = "Deal.lost" and at >= "2026-07-21"', limit: 14 }) // 14 Deals lost in 90 days, each with its free-text reason
return await $.Deal.list({ where: 'status = "Lost"', facets: 'lostReason' }) // the typed reason, ranked by ARR · Not live yet, gaps #2
npx startup-builder do Event.list --json '{"where":"name = \"Deal.lost\" and at >= \"2026-07-21\"","limit":14}'
npx startup-builder do Deal.list --json '{"where":"status = \"Lost\"","facets":"lostReason"}' # Not live yet (gaps #2){
"name": "do",
"arguments": {
"code": "const { events } = await $.Event.list({ where: 'name = \"Deal.lost\" and at >= \"2026-07-21\"', limit: 14 }) // 14 Deals lost in 90 days, each with its free-text reason\nreturn await $.Deal.list({ where: 'status = \"Lost\"', facets: 'lostReason' }) // the typed reason, ranked by ARR · Not live yet, gaps #2"
}
}application/json{
"total": 14,
"facets": {
"lostReason": [
{
"value": "price",
"deals": 5,
"arr": 5827,
"answeredBy": "risk-reversal-offers"
},
{
"value": "no-decision",
"deals": 3,
"arr": 3087,
…
sample · 1 Not live yetPreviewExpected from open Deals
Expected from open Deals: +$1, from 17 open Deals worth $7, if all were won · Not live yet, gaps #2 · sample.
| Deal | Deal status | ARR if won | Buyer decides | Next step | The call that moves it |
|---|---|---|---|---|---|
deal_28 | Proposed | +$347 ARR | 2026-10-24 | Pinecrest Lofts: signing call on Oct 22 | POST /{namespace}/deals/{deal}/win |
deal_33 | Qualified | +$347 ARR | 2026-10-29 | Harbor Row: demo with the owner on Oct 23 | POST /{namespace}/deals/{deal}/propose |
deal_31 | Qualified | +$2, | 2026-10-30 | Northgate Rentals: the two-year Quote once the discount is approved | POST /{namespace}/deals/{deal}/propose |
deal_35 | Open | +$347 ARR | 2026-10-31 | Elm Court: security answers by Oct 27 | POST /{namespace}/deals/{deal}/advance |
It is each open Deal’s ARR times the 90-day win rate of its Deal status. It feeds the cash projection, whose spend state sets what Your Play mix buys.
Which of my open deals are likely to close this month?
curl "https://api.sb/leaseline.app/deals?where=status%20in%20(%22Open%22%2C%20%22Qualified%22%2C%20%22Proposed%22)%20and%20closesOn%20%3C%3D%20%222026-10-31%22&sort=closesOn" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# Not live yet (gaps #2)
curl -X POST https://api.sb/leaseline.app/metrics/query \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"metric":"weighted-pipeline","shape":"trend","window":"90d"}'import { $ } from 'startup-builder'
const { deals } = await $.Deal.list({ where: 'status in ("Open", "Qualified", "Proposed") and closesOn <= "2026-10-31"', sort: 'closesOn' }) // open Deals the buyer expects to decide this month
return await $.Metric.query({ metric: 'weighted-pipeline', shape: 'trend', window: '90d' }) // each open Deal’s ARR times its status’s 90-day odds · Not live yet, gaps #2
npx startup-builder do Deal.list --json '{"where":"status in (\"Open\", \"Qualified\", \"Proposed\") and closesOn <= \"2026-10-31\"","sort":"closesOn"}'
npx startup-builder do Metric.query --json '{"metric":"weighted-pipeline","shape":"trend","window":"90d"}' # Not live yet (gaps #2){
"name": "do",
"arguments": {
"code": "const { deals } = await $.Deal.list({ where: 'status in (\"Open\", \"Qualified\", \"Proposed\") and closesOn <= \"2026-10-31\"', sort: 'closesOn' }) // open Deals the buyer expects to decide this month\nreturn await $.Metric.query({ metric: 'weighted-pipeline', shape: 'trend', window: '90d' }) // each open Deal’s ARR times its status’s 90-day odds · Not live yet, gaps #2"
}
}application/json{
"deals": [
{
"$id": "https: //api.sb/ leaseline.app/ deals/ deal_28",
"$type": "Deal",
"status": "Proposed",
"value": {
"amount": 348,
"currency": "USD"
},
"closesOn": "2026-10-24",
"statusSince": "2026-10-16T14: 10: 00Z",
"nextStep": "Pinecrest Lofts: signing call on Oct 22"
},
…
sample · 1 Not live yetPreviewSeat scorecards
Your number · 4 of 6 · target 6 by Dec 31 · gap 2 · ■■■□ Likely · n 31
$.Metric.query({ metric: · sample
| Title | Its number | Now | Target | Short by |
|---|---|---|---|---|
| You, as head of sales | ARR added each week | +$1, | +$1, by Dec 31 | $412 ARR |
| SDR | Deals and trials opened a week | 4 | 6 by Dec 31 | 2 |
| AE | New paying Customers from Deals, a week | 2 | 3 by Dec 31 | 1 |
| CSM | Net revenue retention on its book | 85 % | 100 % by Dec 31 | 15 points |
Coverage, how many times open Deals cover a gap, prints once the hub holds it (Not live yet, gaps #2).
How far am I from my target this quarter?
curl "https://api.sb/leaseline.app/key-results?where=deadline%20%3D%20%222026-12-31%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# 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":"opened-deals-and-trials","shape":"trend","window":"7d"}'import { $ } from 'startup-builder'
const { keyResults } = await $.KeyResult.list({ where: 'deadline = "2026-12-31"' }) // the quarter’s four Key Results, three of them held by a seat
return await $.Metric.query({ metric: 'opened-deals-and-trials', shape: 'trend', window: '7d' }) // the SDR’s number this week · Not live yet, gaps #4
npx startup-builder do KeyResult.list --json '{"where":"deadline = \"2026-12-31\""}'
npx startup-builder do Metric.query --json '{"metric":"opened-deals-and-trials","shape":"trend","window":"7d"}' # Not live yet (gaps #4){
"name": "do",
"arguments": {
"code": "const { keyResults } = await $.KeyResult.list({ where: 'deadline = \"2026-12-31\"' }) // the quarter’s four Key Results, three of them held by a seat\nreturn await $.Metric.query({ metric: 'opened-deals-and-trials', shape: 'trend', window: '7d' }) // the SDR’s number this week · Not live yet, gaps #4"
}
}application/json{
"keyResults": [
{
"$id": "https: //api.sb/ leaseline.app/ key-results/ key_result_1",
"$type": "KeyResult",
"metric": "net-new-arr",
"baseline": 0,
"target": 1800,
"deadline": "2026-12-31",
"now": 1388
},
{
"$id": "https: //api.sb/ leaseline.app/ key-results/ key_result_2",
"$type": "KeyResult",
…
sample · 1 Not live yetPreviewThe tree
| Number | This week | Owner | Worked by |
|---|---|---|---|
| ARR | $9, | founder | You, as head of sales |
| ARR added each week | +$1, | founder | You, as head of sales |
| New Customers | +$2, | founder | You, as head of sales |
| People reached | 412 | marketing-lead | Content Lead |
| New Leads | 38 | marketing-lead | SDR |
| Deals and trials opened | 11 | sales-lead | SDR |
| New paying Customers | 6 | sales-lead | AE |
| First order size | +$347 ARR | sales-lead | AE |
| Activation rate | 65 % | team: | Onboarding |
| Referred Customers | 4 % | team: | Partnerships |
| Sent by your other Startups | 0 % | marketing-lead | Partnerships |
| Won back | $0 ARR | customer-lead | Head of customers |
| Upgrades | +$347 ARR | customer-lead | CSM |
| Downgrades | $0 ARR | customer-lead | CSM |
| Lost Customers | −$1, | customer-lead | CSM |
| Month 3 retention | 81 % | team: | Onboarding |
| One-time sales ending | $0 ARR | customer-lead | Head of customers |
| Not yet traced | $0 ARR | founder | You, as head of sales |
Each number links its page, with the Plays, pages and title that move it.
Net revenue retention
| Reading | 90 days | Customers |
|---|---|---|
| Net revenue retention | 85 % | 13 |
| The gross line, before upgrades and win-backs (Not live yet, gaps #4) | 69 % | 13 |
logo-churn | 31 % | 4 of 13 |
85 % net revenue retention · ■■□□ Early sign · n 13 Customers · 90 days
$.Metric.query({ metric: · sample
Credit
| How they first arrived | New ARR this week |
|---|---|
outbound | +$694 ARR |
direct | +$694 ARR |
referral | +$347 ARR |
search | +$347 ARR |
answer | $0 ARR |
ad | $0 ARR |
agent | $0 ARR |
| Not yet traced | $0 ARR |
| New Customers | +$2, |
Credited new ARR plus Not yet traced equals New Customers every week. Credit splits it by page and by Play.
Where Deals wait
| Deal status | Deals | Median days in it | Stall line | Past the stall line | The call that moves it |
|---|---|---|---|---|---|
| Open | 8 | 6 d | 12 d | 1 | POST /{namespace}/deals/{deal}/advance |
| Qualified | 6 | 9 d | 18 d | 1 | POST /{namespace}/deals/{deal}/propose |
| Proposed | 3 | 11 d | 22 d | 1 | POST /{namespace}/deals/{deal}/win |
| Handoff | Window | Waiting now | The call |
|---|---|---|---|
| Marketing Lead to SDR | answered within 5 minutes | first answer 40 s on first-response-time; speed to lead is Not live yet, gaps #6 | POST /{namespace}/leads/{lead}/assign |
| SDR to AE | a call booked within 60 minutes of an Interested reply | 1 · the unanswered-positive Stop holds it | POST /{namespace}/meetings/book |
| AE to Onboarding | the Won Deal starts onboard-customer | 0 | POST /{namespace}/customers/{customer}/activate |
| CSM to AE | an upgrade signal opens a Deal | 1 | POST /{namespace}/deals/{deal}/advance |
Meetings held is Not live yet, gaps #6, so no row counts it.
Your quarter
| Goal or Key Result | Baseline | Now | Target | By |
|---|---|---|---|---|
| Reach $25k ARR by year end | $0 | $9, | $25, | 2026-12-31 |
| ARR added each week, a week | $0 ARR | +$1, | +$1, | 2026-12-31 |
| Deals and trials opened, a week (SDR) | 2 | 4 | 6 | 2026-12-31 |
Ten weeks remain: $15, to go, +$1, a week against a Key Result of +$1,.
How far are we from $25k ARR by year end?
curl https://api.sb/leaseline.app/goals/goal_1 \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
curl https://api.sb/leaseline.app/key-results/key_result_1 \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"import { $ } from 'startup-builder'
const goal = await $.Goal.get({ id: 'goal_1' }) // Reach $25k ARR by year end
return await $.KeyResult.get({ id: 'key_result_1' }) // ARR added each week, against 1, 800
npx startup-builder do Goal.get --json '{"id":"goal_1"}'
npx startup-builder do KeyResult.get --json '{"id":"key_result_1"}'{
"name": "do",
"arguments": {
"code": "const goal = await $.Goal.get({ id: 'goal_1' }) // Reach $25k ARR by year end\nreturn await $.KeyResult.get({ id: 'key_result_1' }) // ARR added each week, against 1,800"
}
}application/json{
"$id": "https: //api.sb/ leaseline.app/ key-results/ key_result_1",
"$type": "KeyResult",
"goal": "goals/ goal_1",
"metric": "net-new-arr",
"baseline": 0,
"target": 1800,
"deadline": "2026-12-31"
}
samplePreviewOffers and Deals
| Page | What it holds | Its first call |
|---|---|---|
| Offers and trials | the price floor, the trial and the first Order | POST /{namespace}/offers/{offer}/publish |
| Quotes and discount approval | a Quote below the floor, and your OK on it | POST /{namespace}/quotes/{quote}/send |
| Open Deals | every Deal status with its count, ARR and call | POST /{namespace}/deals/{deal}/advance |
| Open | 8 Deals | POST /{namespace}/deals/{deal}/advance |
| Qualified | 6 Deals | POST /{namespace}/deals/{deal}/propose |
| Proposed | 3 Deals | POST /{namespace}/deals/{deal}/win |
| Won | 9 Deals in 90 days | POST /{namespace}/customers/{customer}/activate |
| Lost | 14 Deals in 90 days | POST /{namespace}/deals |
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.