The metrics/ folder: one file and what it compiles to
What one metrics/ file holds, what each key compiles to, and the checks that read it.
One metrics/ file is the quarter’s Goal and Key Results, and each leaf Metric with its one parent. SalesBuilder compiles it, and show traces every key.
---
goal: { objective: "Reach $25k ARR by year end", horizon: quarter }
keyResults:
- { metric: net-new-arr, baseline: 0, target: 1800, deadline: 2026-12-31 }
- { metric: opened-deals-and-trials, baseline: 2, target: 6, deadline: 2026-12-31, role: sales/prospector }
---$ npx startup-builder show metrics/q4.mdx
goal { objective: "Reach $25k ARR by year end", horizon: quarter } metrics/q4.mdx:2
keyResults metrics/q4.mdx:3
parent new-leads metrics/positive-reply-rate.mdx:2
owner SalesBuilderleaseline.app/
├── plays/
│ ├── cold-email.mdx
│ ├── warm-intros.mdx
│ ├── signup-interviews.mdx
│ ├── lead-reactivation.mdx
│ └── events-and-webinars.mdx
├── audiences/
│ ├── small-property-managers.mdx
│ ├── lapsed-leads.mdx
│ └── renewal-season.mdx
├── sequences/
│ └── cold-email-thread-founder.mdx
├── funnels/
│ └── signup.mdx
├── workflows/
│ └── outbound.mdx
├── agents/
│ ├── sales/prospector.mdx
│ └── marketing/content-lead.mdx
├── systems/
│ └── sequences.mdx
├── domains/
│ └── leaseline.app.mdx
├── offers/
│ ├── pro.mdx
│ └── team.mdx
├── content/
│ └── webinars/60-day-notices.mdx
├── metrics/ ← this folder
│ ├── q4.mdx
│ └── positive-reply-rate.mdx
└── startups.lockThe file
| Key | On the page | Compiles to | Checked by |
|---|---|---|---|
goal | Goal | the quarter’s objective and horizon | — |
keyResults | Key Results | each a node or leaf with baseline, target and deadline; one with a role lands in that seat’s number | S9 |
parent | Parent | in a leaf’s own file: its one parent, so its chain reaches arr | S1 |
Checks
| Check | It holds | What dev prints |
|---|---|---|
| S1 | The Metric’s parent chain reaches arr: the Result card’s path. | metrics/ |
| S9 | Each Key Result names a node or leaf of the ARR tree, and a seat’s Key Result sits in its band. | agents/ |
Refusals
| Code | Fix, then the call that clears it |
|---|---|
baseline | Read the last 90 days first; thin history reads before-after, not proof. Then POST /{namespace}/metrics/query. |
recompute | Recompute the Metric, then render the log again. Then POST /{namespace}/metrics/query. |
scale-gate | Wait for the holdout readout to win, then raise it. Then POST /{namespace}/metrics/query. |
step-gate | Wait for cost per trial to fall under the line, then sign. Then POST /{namespace}/metrics/query. |
stripe-revenue | Query the revenue from live-mode Stripe, then compile the log. Then POST /{namespace}/metrics/query. |
use-when | Run the Play Not yet when names, or wait until the volume is there. Then POST /{namespace}/metrics/query. |
Calls
You, to your agent
Define the leaf Metric and read its trend.
curl -X POST "https://api.sb/leaseline.app/metrics/define?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Leaseline","query":"Example query","unit":"Example unit"}'
curl -X POST https://api.sb/leaseline.app/metrics/query \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"metric":"positive-reply-rate","shape":"trend","window":"28d"}'import { $ } from 'startup-builder'
await $.Metric.define({ name: 'Leaseline', query: 'Example query', unit: 'Example unit' }, { mode: 'try' }) // metrics/ positive-reply-rate.mdx
return await $.Metric.query({ metric: 'positive-reply-rate', shape: 'trend', window: '28d' }) // parent new-leads, up to arr
npx startup-builder do Metric.define --json '{"name":"Leaseline","query":"Example query","unit":"Example unit"}' --try
npx startup-builder do Metric.query --json '{"metric":"positive-reply-rate","shape":"trend","window":"28d"}'{
"name": "do",
"arguments": {
"code": "await $.Metric.define({ name: 'Leaseline', query: 'Example query', unit: 'Example unit' }, { mode: 'try' }) // metrics/positive-reply-rate.mdx\nreturn await $.Metric.query({ metric: 'positive-reply-rate', shape: 'trend', window: '28d' }) // parent new-leads, up to arr",
"mode": "try"
}
}Response200
application/json{
"rows": {},
"metric": {
"$id": "https: //api.sb/ leaseline.app/ metrics/ metric_1",
"$type": "Metric",
"query": "Example query",
"status": "draft"
},
"action": {
"$id": "https: //api.sb/ leaseline.app/ actions/ action_1",
"operation": "Metric.query",
"status": "done"
}
}
sample| Call | What it does |
|---|---|
POST /{namespace}/metrics/define | Define compiles a Metric’s query against the Startup’s Events and Entries and lists every Event it reads. Emits Metric.defined. |
POST /{namespace}/metrics/query | Query reads Metrics or Events as a trend, funnel, retention curve, path or lifecycle, broken down by any property. Emits Metric.queried. |
POST /{namespace}/metrics/{metric}/watch | Watch alerts the owner of a Metric when it crosses a threshold, by a Task or a Message. Emits Metric.watched. |
Compiled, not written: edit the records it reads (compile table; cap), 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.