Benchmark rows
Scores every company in your niche on one measure and sends each its private row, its gap and one free fix, naming no low scorer.
- Channel
- Email and inboxes
- Kind
- Outreach
Be our SDR for a renewal readiness index: read every property manager’s public pages on how they handle renewals, score them 0 to 100, publish the method and the median, and email each one its own row with one fix it can make for free. Never name anyone outside the top 10, and only with their yes. Show me what’s missing; don’t send.
# Not live yet (gaps #2)
curl -X POST "https://api.sb/leaseline.app/campaigns?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"play":"benchmark-rows"}'import { $ } from 'startup-builder'
return await $.Campaign.create({ play: 'benchmark-rows' }, { mode: 'try' }) // dry run on the day-0 Startup, 2026-08-24 · Not live yet, gaps #2
npx startup-builder do Campaign.create --json '{"play":"benchmark-rows"}' --try # Not live yet (gaps #2){
"name": "do",
"arguments": {
"code": "return await $.Campaign.create({ play: 'benchmark-rows' }, { mode: 'try' }) // dry run on the day-0 Startup, 2026-08-24 · Not live yet, gaps #2",
"mode": "try"
}
}application/json{
"type": "https: //startupbuilder.dev/ errors/ refused",
"title": "Refused",
"status": 422,
"detail": "benchmark-rows can’t start yet: 2 checks fail and 1 warns.",
"checks": [
{
"check": "owned-by-a-person",
"status": "pass",
"observed": "0 of 640 companies have a Lead or open Deal a person owns",
"doc": "/plays/ benchmark-rows#check-owned-by-a-person",
"then": "Task.assign"
},
{
…
Hand our latest ready row to the SDR agent and show me what it chose and why. Don’t send.
curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Monitor.checked%22%2C%20%22Form.submitted%22)&limit=1" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# Not live yet (gaps #7)
curl -X POST "https://api.sb/leaseline.app/triggers/fire?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"like":"event_1423"}'
# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/decisions?where=on%20%3D%20%22organization_144%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/actions?where=on%20%3D%20%22organization_144%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"import { $ } from 'startup-builder'
const { events: [last] } = await $.Event.list({ where: 'name in ("Monitor.checked", "Form.submitted")', limit: 1 }) // { play } compiled to this Play’s Trigger Events
const { subject } = await $.Trigger.fire({ like: last.$id }, { mode: 'try' }) // the same Event again, in try mode · Not live yet, gaps #7
const { decisions: [decision] } = await $.Decision.list({ where: `on = "${subject}"` }) // what it chose, and why · Not live yet, gaps #7
const { actions } = await $.Action.list({ where: `on = "${subject}"` }) // what it did, with its checks · Not live yet, gaps #7
return { decision, actions }
npx startup-builder do Event.list --json '{"where":"name in (\"Monitor.checked\", \"Form.submitted\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_1423"}' --try # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"organization_144\""}' # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"organization_144\""}' # Not live yet (gaps #7){
"name": "do",
"arguments": {
"code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Monitor.checked\", \"Form.submitted\")', limit: 1 }) // { play } compiled to this Play’s Trigger Events\nconst { subject } = await $.Trigger.fire({ like: last.$id }, { mode: 'try' }) // the same Event again, in try mode · Not live yet, gaps #7\nconst { decisions: [decision] } = await $.Decision.list({ where: `on = \"${subject}\"` }) // what it chose, and why · Not live yet, gaps #7\nconst { actions } = await $.Action.list({ where: `on = \"${subject}\"` }) // what it did, with its checks · Not live yet, gaps #7\nreturn { decision, actions }",
"mode": "try"
}
}application/json{
"decision": {
"$id": "https: //api.sb/ leaseline.app/ decisions/ decision_495",
"$type": "Decision",
"choice": "send-the-row",
"reason": "Brookstone Rentals scores 41 against the median 58; its renewals page names no notice date, a fix it can copy from the method page, and its operations lead has a verified address",
"stakes": "low",
"options": [
"send-the-row",
"send-as-you",
"ask-to-be-named",
"send-the-movement",
"re-read-it",
"not-now"
…
sample · 4 Not live yetPreviewOn POST /{namespace}/processes/{process}/enableOff POST /{namespace}/processes/{process}/pauseTake this one POST /{namespace}/tasks/{task}/claim
action_3 is the row note to Brookstone’s operations lead, held 2 minutes, with a link to its private row page; the row page is noindex, and only Brookstone sees its own score.
+7.9 acting per 100 rows · ■□□□ Too early to tell · n 169 matured · path → arr
| Try another: the company | Choice | Reason |
|---|---|---|
| Brookstone Rentals: 41 against a median of 58, no notice date on its renewals page | send-the-row | a gap, a free fix, and a verified address |
| Calloway Residential: 72, ranked 4th | ask-to-be-named | in the top 10: it asks for a yes to name them |
| Northgate Lettings: 49 to 61 after adding online renewal | send-the-movement | up 12 points and 21 percentiles |
| Harlow Lettings: “Your reading of our fees page is wrong” | re-read-it | a disputed reading |
| Wrenfield Rentals: a Customer | not-now | Customers get no row note |
- 1 h week 1, then 20 min a week
- Your time
- Day 5: first company ships its fix
- First signal
Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Row action rate → New Leads → New Customers ↑ → ARR added each week → ARR
Worth’s formula: about +7.9 acting companies per 100 rows × 95 rows a month × 0.43 actions to Leads × 0.12 Leads to paid × $650 first order ARR ≈ +$250. Under First signal: About 4 months to tell a +8 lift at 95 rows a month with 10 % held back; at 300 companies, the 30-day read pools with Other Startups’ results.
sample · api.sb/
Runs inside: Proof-of-work outreach, as one of its options; this page keeps its own record and URL.
Start it this week
npx startup-builder add play benchmark-rows # 1 · writes plays/benchmark-rows.mdx (Not live yet, gaps #5)
# 2 · dry run; below, its 422 on the day-0 Startup (clock at 2026-08-24)
curl -X POST "https://api.sb/leaseline.app/campaigns?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"play":"benchmark-rows"}'
# 3 · keep it, at You approve each one for the first 25 rows
curl -X POST https://api.sb/leaseline.app/campaigns \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"play":"benchmark-rows"}'
# on
curl -X POST https://api.sb/leaseline.app/processes/outbound/enable \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# off: no row note sends; the index stays published
curl -X POST https://api.sb/leaseline.app/processes/outbound/pause \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"| Check | Fix | Then |
|---|---|---|
owned-by-a-person pass · Not live yet, gaps #8 | 0 of 640 companies have a Lead or open Deal a person owns | POST /{namespace}/tasks/{task}/assign |
use-when pass · Not live yet, gaps #8 | 640 property managers with public renewal pages | POST /{namespace}/metrics/query |
baseline warn · Not live yet, gaps #8 | n 640: the held-back 10 % is your comparison from day 1. | POST /{namespace}/metrics/query |
approved-pages fail · Not live yet, gaps #8 | Publish the method page; every row links to it. | POST /{namespace}/sites/{site}/publish |
domain-authenticated pass | leaseline.app: 3 of 3 records resolve | POST /{namespace}/domains/{domain}/authenticate |
mailbox-warm fail · Not live yet, gaps #1 | Wait for the warm-up to finish, 14 days from day 0; the index publishes meanwhile. | POST /{namespace}/mailboxes/{mailbox}/warm |
role-unstaffed warn · Not live yet, gaps #8 | Staff the AE, or leave it: replies come to you. | POST /{namespace}/roles/{role}/staff |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "benchmark-rows can’t start yet: 2 checks fail and 1 warns.",
"checks": [
{
"check": "owned-by-a-person",
"status": "pass",
"observed": "0 of 640 companies have a Lead or open Deal a person owns",
"doc": "/plays/benchmark-rows#check-owned-by-a-person",
"then": "Task.assign"
},
{
"check": "use-when",
"status": "pass",
"observed": "640 property managers with public renewal pages",
"doc": "/plays/benchmark-rows#check-use-when",
"then": "Metric.query"
},
{
"check": "baseline",
"status": "warn",
"observed": "0.8 hand-raises per 100 of these companies in 90 days, n 640",
"doc": "/plays/benchmark-rows#check-baseline",
"fix": "n 640: the held-back 10 % is your comparison from day 1.",
"then": "Metric.query"
},
{
"check": "approved-pages",
"status": "fail",
"doc": "/plays/benchmark-rows#check-approved-pages",
"fix": "Publish the method page; every row links to it.",
"then": "Site.publish"
},
{
"check": "domain-authenticated",
"status": "pass",
"observed": "leaseline.app: 3 of 3 records resolve",
"doc": "/plays/benchmark-rows#check-domain-authenticated",
"then": "Domain.authenticate"
},
{
"check": "mailbox-warm",
"status": "fail",
"doc": "/plays/benchmark-rows#check-mailbox-warm",
"fix": "Wait for the warm-up to finish, 14 days from day 0; the index publishes meanwhile.",
"then": "Mailbox.warm"
},
{
"check": "role-unstaffed",
"status": "warn",
"roles": [
"sales-lead"
],
"owned": 0,
"doc": "/plays/benchmark-rows#check-role-unstaffed",
"fix": "Staff the AE, or leave it: replies come to you.",
"then": "Role.staff"
}
]
}It says no, says why, and names the fix.
| Check | Fix (one call) | Your time | Wait |
|---|---|---|---|
owned-by-a-person · use-when · baseline · pass · pass: 640 companies · warn: 0.8 per 100 (gaps #8) | POST /{namespace}/metrics/query on row-action-rate | 1 min | none |
approved-pages · fail: no method page (Not live yet) | POST /{namespace}/sites/{site}/publish the method, the aggregate and the top 10 | 25 min | none |
domain-authenticated · mailbox-warm · pass · fail: sdr@ and rows@ warmed 0 of 14 days | POST /{namespace}/mailboxes/{mailbox}/warm | 2 min | 14 days |
role-unstaffed · warn (gaps #8): no AE, so replies come to you | POST /{namespace}/roles/{role}/staff, or leave it | 0 min | none |
Your number · Before 0.8 hand-raises per 100 · Now 13.8 acting per 100 rows · Target 15 by Dec 31 · ■□□□ Too early to tell · n 169 matured · held back at random
Each reading costs $0.01 a page, at most 10 pages a company; each row note costs $0.04 from your Domain (Worker cost), and contact data for the role that owns the fix costs $0.02 (POST /{namespace}/contacts/{contact}/enrich). That comes to about $6 a month at 95 rows and a weekly re-read.
When it starts
| When | Trigger id | Starts on | Window | How it gets in |
|---|---|---|---|---|
| A row passes the gap floor and the fix test, with a verified address | row-ready | state on Monitor.checked where gap over the floor, a reproducible fix, and a verified address for the owning role | the note within 1 d; dropped after 7 d | the weekly POST /{namespace}/monitors/{monitor}/check |
| A re-read moves a company up by a real change | rank-move | threshold on Monitor.checked where value moved by the smallest real change, and percentile by 5 points or more | the note within 24 h, or dropped | the same |
| A company asks for a re-read | regrade-request | event on Form.submitted where form = 'regrade', from the row Page or a reply | within 24 h | the row page’s Form, through POST /{namespace}/forms |
+ 5 more for index dates, public changes, live fixes, disputes and read-me requests (?lanes=) |
{
"$id": "https://api.sb/leaseline.app/events/event_1423" ,
"$type": "Event",
"name": "Monitor.checked",
"at": "2026-10-19T06:30:19Z" ,
"seq": 1423,
"data": {
"monitor": "renewal-readiness",
"organization": {
"$id": "https://api.sb/leaseline.app/organizations/organization_144" ,
"name": "Brookstone Rentals"
},
"score": 41,
"median": 58,
"gap": 17,
"fix": "state the notice date on the renewals page"
}
}# each delivery is signed with the Watch’s secret
curl -X POST https://api.sb/leaseline.app/watches \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"selector":"Monitor.checked","url":"https://leaseline.app/hooks/sb"}'
npx startup-builder trigger Monitor.checked --like event_1423 # replays the ready row
npx startup-builder clock advance --by 7d # runs the weekly re-read, which finds a fix if it shipped Who runs it
SDR · your agent (role:, worker_2) holds the Key Result. It hands replies to AE · Priya Shah (person) (role:, worker_3, staffed on day 9), and top-tier rows, re-reads that change a score and the top 10 to you (Dana Okafor, person, worker_1). Your number links to Did it work.
| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
|---|---|---|---|---|---|
| Send their row · Send the movement | It does it, you can stop it | 25 of 25 approved unedited, then 166 of 166 | met on day 19 (the first 25 rows); capped here | a dispute or removal Stop → You approve each one | POST /{namespace}/actions/{action}/cancel inside the hold |
| Send it as you · Ask to be named | You approve each one, on every rung | 6 of 6 · 10 of 10 | never | — | POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse |
| Re-read it | It does it; a changed score waits for you | 4 re-read, 1 changed and approved | — | — | POST /{namespace}/approvals/{approval}/approve |
| Not now | It does it | 389 held | — | — | POST /{namespace}/actions/{action}/redirect |
| Never: a low scorer named in public, a number no reading holds, the index without you named as its steward | To a person, every time | 1 refused, 0 sent | never | — | POST /{namespace}/processes/{process}/pause |
What it reads · What it may call. It reads public pages only, at a rate Limit, and stores each reading with its page and time. A company’s own row goes only to that company; public text names only a top-10 company that said yes. Its tools are POST /{namespace}/messages/{message}/schedule, POST /{namespace}/monitors/{monitor}/check, and any other call becomes an Approval under the Role’s Mandate.
You hold the SDR Key Result: 15 acting companies per 100 rows by 2026-12-31. ← worker, metric
On each ready row, re-read or request, choose one option and say why. ← triggers, chooses
Every number in a note matches a stored reading under 7 days old. ← number-match, fresh-reading
Every row note gives one fix the company can make for free. ← your file, line 4; fix-gate
Name nobody outside the top 10, and them only with their yes. ← your file, line 5; named-score The slow loop. The weekly Decisions kept the 8-point gap floor and the one-fix rule, and no Change was merged in the window, so there is no replay or read-back to print.
Bring your own agent (folded) starts at You approve each one when it trails the seated agent on unmatched numbers, and at It does it, you can stop it when it matches.
npx startup-builder eval --scenario benchmark-rows-191 --agent "claude -p" --seat prospector # seated: 13.8 acting per 100, 0 unmatched numbers, $0.06 a row · yours: 12.9, 2 unmatched → You approve each one What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
|---|---|---|---|---|---|
Send their row send-the-row | a gap, a free fix and a verified address | Agent’s call, with its reason | row-first-touch → row-watch for those who claim the row | on reply, POST /{namespace}/tasks/{task}/assign → AE · Priya Shah, due 60 min · Take this one | you, within 60 min |
Send it as you send-as-you | over 500 units | Your rule (line 6) | row-first-touch-founder from dana@, after your OK | you approve each one · Take this one | you |
Ask to be named ask-to-be-named | in the top 10 | Your rule (line 5) | top-ten-ask: one ask for a yes to be named | nobody | the same |
Send the movement send-the-movement | up by a real change and 5+ percentiles | Your rule (the movement Limit) | row-movement: one note per 30 days, upward only | nobody | the same |
Re-read it re-read-it | a re-read request or a dispute | Agent’s call, with its reason | POST /{namespace}/monitors/{monitor}/check twice, 24 h apart; a changed score waits for you | you, due 2 business days · Take this one | you |
Not now not-now | no fix, under the floor, a Customer or open Deal, or no address | Your rule (the send list) | nothing; the company stays in the index and its aggregate | nobody | the same |
| When they write back | Fixture reply | What happens | Who answers | By when | Call |
|---|---|---|---|---|---|
| Interested | “Fixed the notice date. What else would move us?” | offers times with the AE; the Brief goes to her (When it hands to you) | AE · Priya Shah (person) | 60 min | POST /{namespace}/meetings/book |
| Send me pricing | “What does Leaseline cost for 180 units?” | answers with the Pricing Page’s list Price and one next step | SDR · your agent | 5 min | POST /{namespace}/conversations/{conversation}/answer |
| Not now | “Interesting. Maybe after year-end.” | no further note but one upward movement; the row page stays theirs | SDR · your agent | at once | POST /{namespace}/leads/{lead}/nurture |
| Wrong person | “Our web team owns that page.” | asks them to forward the row; it never writes to a name from their text | SDR · your agent | 1 business day | POST /{namespace}/conversations/{conversation}/answer |
| Out of office | an auto-reply until Nov 4 | any movement note waits for the return date, and drops if 24 h pass after a re-read | nobody | the return date | POST /{namespace}/messages/{message}/schedule |
| Question it can’t answer | “How do you weigh online payments against notice timing?” | To you, the question in the Brief; the method page answers what it can | you | 1 business day | POST /{namespace}/conversations/{conversation}/escalate |
| Stop | “Remove us from your index.” | no note sends, and their row leaves every output; the aggregate re-runs without them | nobody | at once | POST /{namespace}/suppressions |
When it hands to you
Replies go to AE · Priya Shah (person); top-tier rows, the top 10 and any re-read that changes a score come to you. Before day 9 every reply came to you. What lands in each queue, as captured: one Brief per Role, then the Approval (brief, draft, route and checks are Not live yet, gaps #7).
[
{
"$id": "https://api.sb/leaseline.app/tasks/task_297" ,
"$type": "Task",
"assignee": "worker_3",
"due": "2026-09-25T15:04:00Z" ,
"on": "lead_72",
"brief": {
"who": "Mei Tanaka, Northgate Lettings" ,
"said": "Fixed the notice date. What else would move us?" ,
"chose": "send-the-row",
"why": "scored 49 against 58; shipped the fix in 6 days" ,
"row": "61 after the re-read, up 21 percentiles" ,
"never": [
"another company’s score",
"a price beyond the list"
]
}
},
{
"$id": "https://api.sb/leaseline.app/tasks/task_301" ,
"$type": "Task",
"assignee": "worker_1",
"due": "2026-10-02T17:00:00Z" ,
"on": "organization_151",
"brief": {
"who": "Harlow Lettings",
"said": "Your reading of our fees page is wrong.",
"chose": "re-read-it",
"why": "a disputed reading",
"reread": "2 reads 24 h apart: 44 and 52; their fees page changed on Sep 29"
}
},
{
"$id": "https://api.sb/leaseline.app/approvals/approval_86" ,
"$type": "Approval",
"verdict": null,
"deadline": "2026-10-02T17:00:00Z" ,
"draft": "Harlow’s row moves from 44 to 52 after the Sep 29 change to its fees page.",
"route": "re-read-it",
"checks": [
{
"check": "number-match",
"status": "pass"
},
{
"check": "fresh-reading",
"status": "pass"
}
]
}
]Each receiver answers with POST /{namespace}/tasks/{task}/claim (Take this one), POST /{namespace}/tasks/{task}/return with a reason, POST /{namespace}/actions/{action}/redirect, POST /{namespace}/approvals/{approval}/approve or POST /{namespace}/approvals/{approval}/refuse, each a one-call window, and an Approval unanswered by its deadline expires and the score stays as read. Once a person claims, the row notes to that company stop, and every reply goes to the claimer’s queue. A Task nobody claims by its due time goes up a Role through POST /{namespace}/tasks/{task}/escalate (AE to you), and person-promise fires.
On time: AE · Priya Shah, 8 of 8 replies claimed within 60 min, median 26 min, ■■□□ Early sign, n 8; you, 17 of 17 approvals by their deadline, n 17. Your queue: counted by kind: in weeks 1 to 3, 25 row approvals, the method page and 10 top-10 asks; since, about 1 hand-off and 1 re-read a week.
The fixed line: POST /{namespace}/deals/{deal}/assign sets owner to the receiving person, the agent is never a Deal’s owner, and Meetings land on the owner’s calendar under the owner’s name.
When it won’t send
| Check (refuses one send) | Refuses | Fix | Then |
|---|---|---|---|
owned-by-a-person (rule j; Not live yet, gaps #8) | a row note to a Contact whose Lead or open Deal a person owns | Leave it to the person who owns it: the draft waits in their queue. | POST /{namespace}/tasks/{task}/assign |
number-match | a number in a row, note or post that matches no stored reading | Use the stored reading, or re-read the page first. | POST /{namespace}/monitors/{monitor}/check |
fresh-reading | a note that states a reading over 7 days old | Re-read the company; the note waits for it. | POST /{namespace}/monitors/{monitor}/check |
fix-gate | a row with no fix anyone can reproduce, or a gap under the smallest real change | Leave it in the index; it gets no note. | GET /{namespace}/decisions |
named-score | public text that names a company outside the top 10, or one with no yes on file | Show the aggregate only, or ask the top 10 for their yes. | POST /{namespace}/messages/{message}/schedule |
cell-floor | a published group of fewer than 10 companies | Merge it into a larger group, or cut it. | PATCH /{namespace}/pages/{id} |
self-exclusion | an index that includes you, your other Startups or a named rival | Drop them from the census and re-run it. | POST /{namespace}/monitors/{monitor}/check |
steward-line | a page, chart, file or note that omits that you run the index and sell in the category | Keep the fixed steward line on every output. | PATCH /{namespace}/pages/{id} |
method | any reading or note under a method that fails a method check | Fix the method page; nothing reads until it passes. | POST /{namespace}/sites/{site}/publish |
movement-limit | a 2nd movement note in 30 days, a downward one, or one after 3 unanswered | Wait for the window; downward moves never send. | POST /{namespace}/messages/{message}/schedule |
late-note | a movement note more than 24 h after its re-read | It drops; the next re-read starts a new one. | POST /{namespace}/monitors/{monitor}/check |
| Stop (pauses the Play) | Pauses when | Then |
|---|---|---|
collection-fence | a request outside public pages or the rate Limit | collection halts until you review it |
unreadable | over 10 % of companies cannot be read | the index and every row hold |
dispute | upheld disputes pass 2 % of rows in 30 days, after 100 | row notes pause and the method returns to you |
removal | removal asks pass 3 % of rows delivered, after 100 | row notes pause |
row-action-floor | under 3 acting per 100 after 300 delivered | row notes stop; the index keeps publishing |
sending-stop-loss | bounces, complaints, spam placement or a blocklist hit pass their lines | that Mailbox pauses; POST /{namespace}/mailboxes/{mailbox}/rest |
kill-switch | you press it | every Tactic pauses; POST /{namespace}/processes/{process}/enable after review |
The captured refusal, then the Stop that fired as a Campaign.paused reason (Not live yet, gaps #2); the note that sent instead is the second thread in Example.
| Check | Fix | Then |
|---|---|---|
number-match fail | Use the stored reading, or re-read the page first. | POST /{namespace}/monitors/{monitor}/check |
fresh-reading pass | — | POST /{namespace}/monitors/{monitor}/check |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "The row note to Oak Hill Management did not send: it states a median the readings do not hold.",
"checks": [
{
"check": "number-match",
"status": "fail",
"observed": "the note said 60; the stored median is 58",
"doc": "/plays/benchmark-rows#check-number-match",
"fix": "Use the stored reading, or re-read the page first.",
"then": "Monitor.check"
},
{
"check": "fresh-reading",
"status": "pass"
}
]
}It says no, says why, and names the fix.
[
{
"reason": {
"limit": "sending-stop-loss",
"observed": "2.3 % bounces on rows@leaseline.app",
"bound": "2 %",
"window": "1 day"
},
"doc": "/plays/benchmark-rows#stop-sending-stop-loss" ,
"mailbox": "rows@leaseline.app"
}
]Did it work
Both halves come from the same random draw: 191 companies sent their row and 21 held back (rules m–o); a company counts once 30 days have passed since its row. Each figure’s call is POST /{namespace}/metrics/query by node and by arm; every node above the leaf is Not live yet (gaps #4).
+7.9 acting per 100 rows · ■□□□ Too early to tell · n 169 matured · path → arr
- Chose
- send-the-row
- Why
- Brookstone Rentals scores 41 against the median 58; its renewals page names no notice date, a fix it can copy from the method page, and its operations lead has a verified address
- From
- send-the-row · send-as-you · ask-to-be-named · send-the-movement · re-read-it · not-now
- Expects
- Brookstone ships the fix, replies or claims its row within 30d
- Reopens when
- no row action at organization_144 within 30d
- By
- worker_2
JSON
{
"$id": "decisions/decision_495",
"$type": "Decision",
"choice": "send-the-row",
"reason": "Brookstone Rentals scores 41 against the median 58; its renewals page names no notice date, a fix it can copy from the method page, and its operations lead has a verified address",
"stakes": "low",
"options": [
"send-the-row",
"send-as-you",
"ask-to-be-named",
"send-the-movement",
"re-read-it",
"not-now"
],
"by": "worker_2",
"on": "organization_144",
"expected": "Brookstone ships the fix, replies or claims its row within 30d",
"reopensWhen": "no row action at organization_144 within 30d"
}| Node | Agent half | Your half | Added | Confidence | Basis |
|---|---|---|---|---|---|
row-action-rate | 13.8 acting per 100 rows (21 of 152) | 5.9 hand-raises per 100 held back (1 of 17) | +7.9 [−4.6, +20.4] | ■□□□ Too early to tell | held back at random · n 169 matured · 43 still maturing · Other Startups’ results +9.2, ■■□□ Early sign, n 2,870 |
new-leads | 5.9 per 100 (9 of 152) | 0 (0 of 17) | +5.9 [−1.4, +13.2] | ■□□□ Too early to tell | the same assignment |
new-arr ↑ | $945, 1 Customer on Team · Booked: credited, not all caused by this Play | $0 | +$622 per 100 rows [−$600, +$1,840] · Worth about +$250 a month | ■□□□ Too early to tell (1 vs 0 Customers) | the same · 2 Deals still deciding |
net-new-arr · arr | +$945 | $0 | +$622 per 100 rows, carried: it moves no other bridge term | ■□□□ Too early to tell | carried from new-arr |
The leaf counts companies that, within 30 days of their row, ship a change that moves their score, reply with anything but stop, or claim their row, each once, per 100 rows delivered; a held-back company counts if it writes in first.
How it moves it: each company got its own score, its gap to the median of 58 and one fix it could copy from the method page, and 14 of the first 152 shipped that fix within 30 days.
Guardrails: treated against held back: positive-reply-rate on the row Mailboxes, 6.1 %, and removal asks, 2 of 188 delivered, under the 3 % Stop.
{
"$id": "https://api.sb/leaseline.app/campaigns/benchmark-rows" ,
"$type": "Campaign",
"play": "play:benchmark-rows@1" ,
"path": [
"row-action-rate",
"new-leads",
"new-arr",
"net-new-arr",
"arr"
],
"experiment": {
"metric": "row-action-rate",
"unit": "per-100",
"heldBack": 21,
"decision": null
},
"readout": {
"value": 7.9,
"n": 169,
"maturing": 43,
"interval": [
-4.6,
20.4
],
"readability": "inadmissible",
"basis": "held-back",
"pooled": false
},
"ledger": {
"spend": 9,
"workerCost": 8,
"hours": 3.1,
"wonArr": 945,
"projectedArr": 250,
"paybackDays": 7
}
}Outside benchmark: self-ranked “best of” lists leave out their own publisher in 1 of 4 cases, and readers trust those that do (a 2026 search-quality study).
Before it · After it. Before it: programmatic-seo and aeo make the index page findable. After it: proof-of-work-outreach, which it runs inside, writes the next note once a company engages, and instant-response answers anyone who writes first.
Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days). 640 property managers were read; 26 could not be (4.1 %, under the hold), leaving 614 scored, median 58. After the top 10, 31 Customers, no fix, no address and holds, 212 were eligible: 191 got their row and 21 were held back at random. Read every captured thread at ?threads=; three follow the tables.
| What happened, by option | Decisions | Done | Acting companies (what happened) |
|---|---|---|---|
| Send their row · Send it as you | 185 · 6 | 185 sent, 3 bounced · 6 sent | 20 · 1 |
| Ask to be named · Send the movement | 10 · 9 | 6 said yes · 9 sent | — · 4 replied |
| Re-read it · Not now | 4 · 389 | 1 score changed · held | — |
| Sent · held back | 191 · 21 | 188 delivered | 21 of 152 matured · 1 of 17 |
| At 300 companies (the Use it when volume) | about 90 rows | held back at random; pools at 30 days | Other Startups’ results: +9.2 per 100, ■■□□ Early sign, n 2,870 |
| Day | Event | What happened | Your time |
|---|---|---|---|
| 0 | event_1121 Campaign.created | the method from 7 parts; the dry run fails approved-pages and mailbox-warm | 35 min |
| 1 | event_1127 Site.published | the method page, the median and the top 10’s empty slots | 25 min |
| 3 | event_1139 Message.created, first of 10 | the top-10 asks; 6 said yes by day 9 | 10 min |
| 14 | event_1191 Mailbox.warmed | the first rows sent, 25 a day across 2 Mailboxes | — |
| 19 | event_1218 Role.updated | promoted after 25 rows approved unedited: rows send after a 2-minute hold | — |
| 20 | event_1222 Monitor.checked | the first fix live: Northgate added its notice date | — |
| 29 | event_1297 Campaign.paused | sending-stop-loss on rows@: 2.3 % bounces; rested a day, resumed | 5 min |
| 1–56 | event_1140 Task.created, first of 20 | approvals, re-reads and the weekly memo, from the POST /{namespace}/tasks/next log | 1.8 h · total 3.1 h measured, 3.5 h asked |
Leaseline → Mei Tanaka, Northgate Lettings, Sep 12, 08:40 · Subject: Northgate’s renewal readiness: 49 · We read 614 property managers’ public renewal pages; the median is 58.¹ Northgate’s renewals page names no notice date, which is 9 of the 51 points between you and the leader. Adding one line, “we send renewal notices 60 days out”, closes it.² Your private row: leaseline.app/rows/northgate. We run this index and sell renewal software. *Signed with the Role’s disclosure line.*³
Sep 18 · the re-read found the line live: 61, up 21 percentiles; the movement note went the next morning. Sep 25 · “Fixed the notice date. What else would move us?” The Brief went to Priya, and Northgate took Team on Oct 9 (+$945 ARR).
- The median property manager scores 60; Oak Hill scores 47.
+ The median property manager scores 58; Oak Hill scores 47. Harlow Lettings, 2026-10-01 11:05 · “Your reading of our fees page is wrong.” · Leaseline, 11:06 · *Thanks. Dana re-reads every disputed row and replies by tomorrow, 17:00.*⁴ Two reads 24 h apart scored 52, up from 44 after their Sep 29 change; Dana approved the new score at 15:30 (
approval_86).
¹ Every number matches a stored reading under 7 days old. ² One fix the company can make for free. ³ The fixed signature and steward lines, compiled from the Role’s Settings. ⁴ The routed line, compiled from the Role’s Settings.
Your settings
---
holdout: 0.1 # 1 in 10 eligible companies gets no row note, chosen at random
---
Score renewal readiness, 0 to 100, from public pages only; one free fix per row.
Name only the top 10, and only with their yes.
Over 500 units, send the row as Dana, after her OK. $ npx startup-builder show plays/benchmark-rows
holdout 0.1 · the index and fix rule · the top-10 rule · the 500-unit line your file plays/benchmark-rows.mdx:2,4,5,6 (Ask 2026-08-24)
family demand · process outbound · runs inside proof-of-work-outreach · worker role:prospector, owns never · chooses 6 (4 rule, 2 read) · sequences row-first-touch, row-first-touch-founder, row-movement, row-watch, top-ten-ask · approval every-send · checks owned-by-a-person, number-match, fresh-reading, fix-gate, named-score, cell-floor, self-exclusion, steward-line, method, movement-limit, late-note · stops 7 catalog play:benchmark-rows@1 (Not live yet, gaps #5) Every key from the catalog
Compiled, not written: edit the Play record benchmark-rows and its Leaseline run, 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.