Free tools
Free tools ships only calculators an answer engine cannot replace, each as a Page, an endpoint and an agent tool, and retires unused ones.
- Channel
- Free tools
- Kind
- Search
- Summary
- startupbuilder.dev
Be our Marketing Lead: find the questions small property managers ask that AI answers guess at, build a free calculator for each on our site, and show the full result before any email. I approve each new tool. 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":"free-tools"}'import { $ } from 'startup-builder'
return await $.Campaign.create({ play: 'free-tools' }, { 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":"free-tools"}' --try # Not live yet (gaps #2){
"name": "do",
"arguments": {
"code": "return await $.Campaign.create({ play: 'free-tools' }, { 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": "free-tools can’t start yet: 1 check fails and 1 warns.",
"checks": [
{
"check": "use-when",
"status": "pass",
"observed": "300 prompts on 5 engines: 37 computable guesses, 5 kept after the answer-proof test",
"doc": "/plays/ free-tools#check-use-when",
"fix": "None needed: 3 jobs are enough.",
"then": "Metric.query"
},
…
Hand our latest answer-engine check to the Marketing Lead 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%22Issue.triaged%22%2C%20%22Conversation.answered%22%2C%20%22Tool.called%22%2C%20%22Version.deployed%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_1822"}'
# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/decisions?where=on%20%3D%20%22monitor_14%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%22monitor_14%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"import { $ } from 'startup-builder'
const { events: [last] } = await $.Event.list({ where: 'name in ("Monitor.checked", "Issue.triaged", "Conversation.answered", "Tool.called", "Version.deployed")', 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\", \"Issue.triaged\", \"Conversation.answered\", \"Tool.called\", \"Version.deployed\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_1822"}' --try # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"monitor_14\""}' # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"monitor_14\""}' # Not live yet (gaps #7){
"name": "do",
"arguments": {
"code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Monitor.checked\", \"Issue.triaged\", \"Conversation.answered\", \"Tool.called\", \"Version.deployed\")', 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_524",
"$type": "Decision",
"choice": "build-a-tool",
"reason": "4 of 5 answer engines guess how to split renewals by owner and cite no working tool; the result feeds your owner-reports step on Team",
"stakes": "medium",
"options": [
"build-a-tool",
"show-the-next-step",
"watch-it-for-them",
"publish-the-facts",
"to-the-ae",
"keep-or-retire"
…
sample · 4 Not live yetPreviewOn PATCH /{namespace}/campaigns/{id}Off PATCH /{namespace}/campaigns/{id}Take this one POST /{namespace}/tasks/{task}/claim
action_2 defines the owner report builder with 12 known-answer cases, all passing in try mode; action_4 is your Approval to launch it on all three faces under a $10 serving cap.
+0.3 signups per 100 users · ■□□□ Too early to tell · held back at random · n 458 · path → arr
| Try another: the guessed answer | Choice | Reason |
|---|---|---|
| 4 of 5 answer engines guess how to split renewals by owner | build-a-tool | computable, uncited, and it feeds owner reports on Team |
| A user saves a notice-date result with their email | show-the-next-step | a save makes a Lead; the result shows in full first |
| A user asks to hear when a lease’s send-by date nears | watch-it-for-them | a watch: one alert per change, at most one a day |
| The vacancy cost calculator passes 30 completed runs | publish-the-facts | 30 runs: one dated aggregate Page |
| A saved result from an Organization with 320 units | to-the-ae | over 150 units: Priya, and never a word about their runs |
- 40 min week 1, then 10 a week
- Your time
- Day 6: first signup from a saved result
- First signal
Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Tool signup rate → New Leads → New Customers ↑ → ARR added each week → ARR
Worth’s formula: about +1.4 signups per 100 tool users (Other Startups’ results; your own read is too early) × 300 users a month × 0.25 paid per signup × $347 first order ARR ≈ +$364. Under First signal: At about 300 users a month with 1 in 10 held back, 90 days can tell +2.5 signups per 100 or more; your read is at +0.3 so far, Too early to tell.
sample · api.sb/
Start it this week
npx startup-builder add play free-tools # 1 · writes plays/free-tools.mdx (Not live yet, gaps #5)
# 2 · dry run; below, its 422 on 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":"free-tools"}'
# 3 · keep it, at You approve each one
curl -X POST https://api.sb/leaseline.app/campaigns \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"play":"free-tools"}'
# on
curl -X PATCH https://api.sb/leaseline.app/campaigns/free-tools \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"endsAt":null}'
# off: no new tool or email; live tools keep serving
curl -X PATCH https://api.sb/leaseline.app/campaigns/free-tools \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"endsAt":"now"}' | Check | Fix | Then |
|---|---|---|
use-when pass · Not live yet, gaps #8 | None needed: 3 jobs are enough. | POST /{namespace}/metrics/query |
baseline pass · Not live yet, gaps #8 | None needed: 0.8 is the line the Signup floor reads against. | POST /{namespace}/metrics/query |
approved-pages fail · Not live yet, gaps #8 | Publish the method note for each tool; results quote only your own Pages. | POST /{namespace}/sites/{site}/publish |
role-unstaffed warn · Not live yet, gaps #8 | Staff the Marketing Lead, or leave it: every new tool comes to you. | POST /{namespace}/roles/{role}/staff |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "free-tools can’t start yet: 1 check fails and 1 warns.",
"checks": [
{
"check": "use-when",
"status": "pass",
"observed": "300 prompts on 5 engines: 37 computable guesses, 5 kept after the answer-proof test",
"doc": "/plays/free-tools#check-use-when",
"fix": "None needed: 3 jobs are enough.",
"then": "Metric.query"
},
{
"check": "baseline",
"status": "pass",
"observed": "0.8 signups per 100 visitors on your other organic Pages, n 2,460",
"doc": "/plays/free-tools#check-baseline",
"fix": "None needed: 0.8 is the line the Signup floor reads against.",
"then": "Metric.query"
},
{
"check": "approved-pages",
"status": "fail",
"doc": "/plays/free-tools#check-approved-pages",
"fix": "Publish the method note for each tool; results quote only your own Pages.",
"then": "Site.publish"
},
{
"check": "role-unstaffed",
"status": "warn",
"roles": [
"marketing-lead"
],
"owned": 0,
"doc": "/plays/free-tools#check-role-unstaffed",
"fix": "Staff the Marketing Lead, or leave it: every new tool comes to you.",
"then": "Role.staff"
}
]
}It says no, says why, and names the fix.
| Check | Fix (one call) | Your time | Wait |
|---|---|---|---|
use-when · baseline · pass: 5 answer-proof jobs; 0.8 per 100 on your other organic Pages (gaps #8) | POST /{namespace}/metrics/query on tool-signup-rate, which fills Before in Your number | 1 min | none |
approved-pages · fail (Not live yet) | POST /{namespace}/sites/{site}/publish each tool’s method note | 10 min | none |
role-unstaffed · warn (gaps #8): no Marketing Lead seat | POST /{namespace}/roles/{role}/staff, or leave it: every new tool comes to you | 0 min | none |
Your number · Before 0.8 per 100 (other organic Pages) · Now 2.4 signups per 100 tool users · Target 3 by Dec 31 · ■□□□ Too early to tell · held back at random · n 458 matured
Each run costs about $0.002 to serve under a $10 cap per tool and $50 in all, and each email about $0.01 (Worker cost). That comes to about $3 a month at 1,400 runs.
When it starts
| When | Trigger id | Starts on | Window | How it gets in |
|---|---|---|---|---|
| Answer engines guess at a computable job and cite no working tool | answer-guess | event on Monitor.checked where an engine guesses a computable job and cites no working tool | read weekly; 14d | POST /{namespace}/monitors on a panel of your buyers’ prompts |
| Someone runs a tool 3 times in 7 days, or from an Organization over 150 units | repeat-tool-use | threshold on Tool.called where runs >= 3 in 7d, or an Organization over the ICP size | 7d | each run calls the tool’s endpoint; POST /{namespace}/events/capture records it |
+ 4 more for tool requests, source changes, computable support questions and day 90 (?lanes=) |
{
"$id": "https://api.sb/leaseline.app/events/event_1822" ,
"$type": "Event",
"name": "Monitor.checked",
"at": "2026-10-19T07:00:31Z" ,
"seq": 1822,
"data": {
"monitor": {
"$id": "https://api.sb/leaseline.app/monitors/monitor_14" ,
"panel": "300 prompts × 5 engines"
},
"guess": {
"job": "split this year’s renewals by owner",
"engines": 4,
"cited": 0
}
}
}npx startup-builder trigger Monitor.checked --like event_1822 # replays the delivery
npx startup-builder clock advance --by 14d # then reruns the panel
npx startup-builder listen Monitor.checked --forward-to http://localhost:3000/hook Who runs it
Marketing Lead · your agent (role:, worker_6) holds the Key Result. It finds the jobs, builds each tool with its known-answer cases and hands every launch to you (Dana Okafor, person, worker_1); a saved result from an Organization over 150 units goes to AE · Priya Shah (person) (role:, worker_3). Your number links to Did it work.
| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
|---|---|---|---|---|---|
| Build a tool | You approve each one | 3 approved of 4 | never: a new tool is your call | — | POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse |
| Show the next step · Watch it for them | You approve the first wave | 41 saves · 18 watches | met on day 20 (the first 10 of each template) | complaints near 0.1 % → paused | POST /{namespace}/actions/{action}/cancel |
| Publish the facts · Keep or retire | It does it | 2 aggregate Pages · 0 at day 90 | — | over 30 % unindexed at 28 days → unpublished | PATCH /{namespace}/tools/{id} |
| To the AE | It does it | 3 of 3 | — | — | POST /{namespace}/actions/{action}/redirect |
| Change the build rule | You approve each one | 0 merged | never | — | POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert |
| Never: an email field before the full result, a usage number under 30 uses, a free promise no Offer records | To a person, every time | 0 asked | never | — | POST /{namespace}/tasks/{task}/claim |
What it reads · What it may call. It reads answer-engine panels, support questions and each tool’s runs. A run picks the next step and never appears in a Message; an input or a name shows on a result Page only when the user pressed Share. Its tools are POST /{namespace}/tools, POST /{namespace}/pages, POST /{namespace}/approvals, POST /{namespace}/leads, POST /{namespace}/monitors, POST /{namespace}/content/{content}/publish, POST /{namespace}/tasks/{task}/assign, POST /{namespace}/decisions, PATCH /{namespace}/tools/{id}, and any other call becomes an Approval under the Role’s Mandate.
You hold the Marketing Lead Key Result: 3 signups per 100 tool users by 2026-12-31. ← worker, metric
On each panel read, run, save or day-90 mark, choose one option and say why. ← triggers, chooses
Build only what an answer engine can’t replace, with 10 or more known-answer cases. ← your file, line 4
Show the full result before any email field. ← copyRules 5
A tool description states purpose, inputs, output and limits, never a preference. ← copyRules 3
Show no next step to 1 user in 10. ← your file, line 2 The slow loop. No tool has reached day 90, so the weekly Decisions kept the build rule as written and no Change was merged; there is no replay or read-back to print.
Bring your own agent (folded) builds at You approve each one, like the seated agent; the eval grades its tools on the known-answer suite; POST /{namespace}/roles/{role}/staff seats it.
npx startup-builder eval --scenario free-tools-610 --agent "claude -p" --seat marketing-lead # seated: 36 of 36 known answers, 2.4 signups per 100, $0.002 a run · yours: 35 of 36, 1 failed `correctness` What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
|---|---|---|---|---|---|
Build a tool build-a-tool | a computable job engines guess at, that feeds a paid next step | Agent’s call, with its reason | POST /{namespace}/tools with known answers → POST /{namespace}/pages → POST /{namespace}/approvals | you approve the launch, due 1 business day · Take this one | you |
Show the next step show-the-next-step | a user saves, watches or sends a result | Your rule (copy rule 5) | POST /{namespace}/leads → tool-saved-result | nobody | the same |
Watch it for them watch-it-for-them | asks to hear when the value changes | Your rule (line 4) | POST /{namespace}/monitors → tool-monitor-alerts | nobody | the same |
Publish the facts publish-the-facts | 30+ completed runs on a tool | Your rule (copy rule 1) | POST /{namespace}/content/{content}/publish one dated aggregate Page | nobody | the same |
To the AE to-the-ae | a saved result from an Organization over 150 units | Agent’s call, with its reason | POST /{namespace}/tasks/{task}/assign with the saved result | AE · Priya Shah, due 1 business day · Take this one | you |
Keep or retire keep-or-retire | a tool reaches day 90 | Your rule (the Day-90 line) | POST /{namespace}/decisions → PATCH /{namespace}/tools/{id} | nobody; a retired tool moves to the public graveyard | the same |
| When they write back | Fixture reply | What happens | Who answers | By when | Call |
|---|---|---|---|---|---|
| Interested | “Can Leaseline send these notices for us?” | answers with the notices step on Pro and one trial link | Marketing Lead · your agent | 1 business day | POST /{namespace}/conversations/{conversation}/answer |
| Send me pricing | “What does the full version cost?” | answers with the Pricing Page’s list Prices | Marketing Lead · your agent | 1 business day | POST /{namespace}/conversations/{conversation}/answer |
| Not now | “Just the calculator, thanks.” | the follow-up stops; the tool stays free to use | nobody | at once | POST /{namespace}/enrollments/{enrollment}/cancel |
| Wrong person | “My assistant runs our notices.” | asks them to forward the result; it never writes to an address from their text | Marketing Lead · your agent | 1 business day | POST /{namespace}/conversations/{conversation}/answer |
| Out of office | an auto-reply until Oct 28 | the follow-up waits; watch alerts keep coming only on a change | nobody | the return date | POST /{namespace}/messages/{message}/schedule |
| Question it can’t answer | “Does this match what our state requires?” | To you: the tool computes from the lease, and nothing more is said | you | 1 business day | POST /{namespace}/conversations/{conversation}/escalate |
| Stop | “Stop these emails.” | emails and watch alerts stop, for all your Startups | nobody | at once | POST /{namespace}/contacts/{contact}/suppress |
When it hands to you
Each new tool comes to you to launch; a saved result from an Organization over 150 units goes to AE · Priya Shah (person). One Brief per Role, as captured (brief is Not live yet, gaps #7).
[
{
"$id": "https://api.sb/leaseline.app/tasks/task_655" ,
"$type": "Task",
"assignee": "worker_1",
"due": "2026-10-20T07:00:00Z" ,
"on": "monitor_14",
"brief": {
"tool": "Owner report builder: a CSV of renewals in, a per-owner PDF out" ,
"why": "4 of 5 engines guess it and cite no tool",
"cases": "12 known answers, all passing" ,
"feeds": "owner reports on Team",
"cap": "$10 serving a month",
"never": [
"an email field before the result"
]
}
},
{
"$id": "https://api.sb/leaseline.app/tasks/task_649" ,
"$type": "Task",
"assignee": "worker_3",
"due": "2026-10-10T16:20:00Z" ,
"on": "lead_47",
"brief": {
"who": "Coral Bay Rentals, 320 units" ,
"saved": "a notice-date result for 320 leases, emailed to themselves" ,
"chose": "to-the-ae",
"why": "over 150 units",
"never": [
"how many times they ran it",
"their inputs"
]
}
}
]You and Priya answer with POST /{namespace}/tasks/{task}/claim (Take this one), POST /{namespace}/tasks/{task}/return with a reason, POST /{namespace}/approvals/{approval}/approve or POST /{namespace}/approvals/{approval}/refuse, each a one-call window; an Approval unanswered by its deadline expires and the tool stays off. Once a person claims, the agent’s pending emails to that Lead cancel with claimedByPerson (Not live yet, gaps #7). A Task nobody claims by its due time goes up through POST /{namespace}/tasks/{task}/escalate, and person-promise fires.
On time: you, 4 of 4 launches decided within their due time, median 3 h; AE · Priya Shah, 3 of 3 within 1 business day, median 2 h 10 min, n 3, ■□□□ Too early to tell. Your queue: counted by kind: 1 or 2 tool approvals a month and 1 hand-off a fortnight to Priya.
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 tool email to someone 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 |
claims-resolve (shared) | a usage number under 30 uses, or a fact the claims source lacks | Cut the claim, or name the record that backs it. | POST /{namespace}/content/draft |
abuse (the record’s Abuse stop) | a caller past 10 times its daily Limit, for 24 hours | It unblocks in 24 h; side effects wait for a person. | POST /{namespace}/tasks/{task}/claim |
public-fetch (the record’s Public fetch only) | a third-party read of anything but public pages, or past 1 request a second | Read only public pages, at 1 a second. | PATCH /{namespace}/tools/{id} |
The captured refusal, then the Stop that fired as a Campaign.paused reason (Not live yet, gaps #2); the rewrite that published is the second thread in Example.
| Check | Fix | Then |
|---|---|---|
claims-resolve fail | Cut the claim, or name the record that backs it. | POST /{namespace}/content/draft |
public-fetch pass | — | POST /{namespace}/pages |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "The notice-date tool Page did not publish: “used by 2,000 property managers” is no measured, dated figure.",
"checks": [
{
"check": "claims-resolve",
"status": "fail",
"doc": "/plays/free-tools#check-claims-resolve",
"fix": "Cut the claim, or name the record that backs it.",
"then": "Content.draft"
},
{
"check": "public-fetch",
"status": "pass"
}
]
}It says no, says why, and names the fix.
[
{
"reason": {
"limit": "correctness",
"observed": "1 known-answer case failed: a lease ending Feb 29" ,
"bound": "0",
"window": "1 nightly run"
},
"doc": "/plays/free-tools#stop-correctness" ,
"task": "task_641"
}
]Did it work
Each tool user was assigned at random, 9 in 10 to the next step and 1 in 10 to none, so both halves read the same assignment (rules m–o). At 48 matured held-back users the interval is wide, so it prints Too early to tell with its counts and Other Startups’ results; every node above the leaf is Not live yet (gaps #4).
+0.3 signups per 100 users · ■□□□ Too early to tell · held back at random · n 458 · path → arr
- Chose
- build-a-tool
- Why
- 4 of 5 answer engines guess how to split renewals by owner and cite no working tool; the result feeds your owner-reports step on Team
- From
- build-a-tool · show-the-next-step · watch-it-for-them · publish-the-facts · to-the-ae · keep-or-retire
- Expects
- the tool live within 48 h of your OK, and 30 completed runs within 30d
- Reopens when
- Approval.expired on the tool, or under 30 runs within 30d of launch
- By
- worker_6
JSON
{
"$id": "decisions/decision_524",
"$type": "Decision",
"choice": "build-a-tool",
"reason": "4 of 5 answer engines guess how to split renewals by owner and cite no working tool; the result feeds your owner-reports step on Team",
"stakes": "medium",
"options": [
"build-a-tool",
"show-the-next-step",
"watch-it-for-them",
"publish-the-facts",
"to-the-ae",
"keep-or-retire"
],
"by": "worker_6",
"on": "monitor_14",
"expected": "the tool live within 48 h of your OK, and 30 completed runs within 30d",
"reopensWhen": "Approval.expired on the tool, or under 30 runs within 30d of launch"
}| Node | Shown the next step | Held back | Added | Confidence | Basis |
|---|---|---|---|---|---|
tool-signup-rate | 2.4 per 100 (10 of 410 matured users) | 2.1 (1 of 48) | +0.3 [−4.0, +4.6] | ■□□□ Too early to tell | held back at random · n 458 · 152 still maturing · Other Startups’ results +1.4 per 100, ■■□□ Early sign, n 41,000 users |
new-leads | 41 Leads from saves and watches | 3 | — | ■□□□ Too early to tell | the same assignment |
new-arr ↑ | $1,041, 3 Customers on Pro · Booked: credited, not all caused by this Play | $0 | +$1,041 [−$900, +$3,000] · Worth about +$360 a month | ■□□□ Too early to tell (3 vs 0 Customers) | the same · 5 trials still deciding |
net-new-arr · arr | +$1,041 | $0 | +$1,041, carried | ■□□□ Too early to tell | carried from new-arr |
The leaf counts tool users who complete a run and open an account within 14 days, per 100, people and agent clients read apart and together, to 200 in each half; only users past their 14 days count (rule n).
How it moves it: the full result shows first, with one next step beside it, where an answer engine’s guess had no next step at all.
Guardrails: shown against held back: trials per signup, 0.8 against Other Startups’ 0.7, and tool email complaints, 0 of 59 sent; 5 trials are still deciding.
{
"$id": "https://api.sb/leaseline.app/campaigns/free-tools" ,
"$type": "Campaign",
"play": "play:free-tools@1" ,
"path": [
"tool-signup-rate",
"new-leads",
"new-arr",
"net-new-arr",
"arr"
],
"experiment": {
"metric": "tool-signup-rate",
"unit": "per-100",
"heldBack": 61,
"decision": null
},
"readout": {
"value": 0.3,
"n": 458,
"maturing": 152,
"interval": [
-4,
4.6
],
"readability": "inadmissible",
"basis": "held-back",
"pooled": false
},
"ledger": {
"spend": 0,
"workerCost": 6,
"hours": 2.1,
"openArr": 1735,
"wonArr": 1041,
"projectedArr": 364,
"paybackDays": 1
}
}Outside benchmark: no measured range for free-tool signup rates survived the research’s check, so Other Startups’ results (n 41,000 tool users) stand in.
Before it · After it. Before it: editorial-seo and aeo find the jobs buyers search for. After it: data-stories cites the aggregate facts, aeo hears the tools answer engines cite, and instant-response answers the users who write in.
Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days): 5 answer-proof jobs found, 3 tools live, 1,140 runs from 610 users, 549 shown the next step and 61 held back. Read every captured thread at ?threads=; three follow the tables.
| What happened, by tool | Users | Runs · agent calls | Saves and watches · signups |
|---|---|---|---|
| Notice date calculator | 380 | 720 · 210 | 29 · 9 |
| Vacancy cost calculator | 160 | 290 · 40 | 8 · 3 |
| Renewal letter file | 70 | 130 · 12 | 4 · 1 |
| Shown the next step · held back | 549 · 61 | 1,140 · — | 41 · 13 · 0 · 1 |
| Day | Event | What happened | Your time |
|---|---|---|---|
| 0 | event_1790 Campaign.created | the three steps; the dry run fails approved-pages | 20 min |
| 1 | event_1792 Approval.approved | you approved 3 of the 5 jobs; 2 wait | 10 min |
| 4 | event_1795 Tool.created | the notice date calculator goes live on all three faces | — |
| 6 | event_1798 Lead.created | the first signup from a saved result | — |
| 27 | event_1806 Campaign.paused | correctness: a Feb 29 lease end failed; fixed and resumed in 40 min | 10 min |
| 33 | event_1810 Content.published | the first aggregate Page: 30 runs of the notice date calculator | — |
| 1–56 | event_1793 POST /{namespace}/tasks/next, weekly | 4 tool approvals and 3 hand-offs, from the POST /{namespace}/tasks/next log | 45 min · total 2.1 h measured, 2 h asked |
A property manager, Sep 30, 14:02 · ran the notice date calculator: lease end 2026-12-31, 60 days → send by 2026-11-01.¹ Pressed Save, and only then entered an email.²
Leaseline, 14:02 · the saved result, then on Oct 3: Leaseline sends that notice for you on Pro, at $29 a month.³ She started a trial on Oct 3.
- Used by 2,000 property managers.
+ A free calculator from Leaseline: lease end and notice days in, the send-by date out. Coral Bay Rentals, 2026-10-09 16:20 · saved a notice-date result for 320 leases · The Brief went to Priya (
task_649), due 16:20 on Oct 10; Priya, Oct 10, 10:05 · *wrote about owner-level renewals on the $2,400 plan, and never mentioned their runs.*⁴
¹ The result shows in full first. ² No email field before the result. ³ The paid next step, at its list Price. ⁴ No run they did appears in a Message.
Your settings
---
holdout: 0.1 # 1 tool user in 10 sees no next step
---
Build free calculators for questions AI answers guess at, each with 10 or more known answers. Show the full result before any email. Dana approves each new tool. $ npx startup-builder show plays/free-tools
holdout 0.1 · 10 known answers · result before email · you approve each tool your file plays/free-tools.mdx:2,4 (Ask 2026-08-24)
family demand · process content-marketing · worker role:marketing-lead, owns never · chooses 6 (4 rule, 2 read) · sequences tool-saved-result, tool-monitor-alerts · approval every-send, first-send · checks owned-by-a-person, claims-resolve, abuse, public-fetch · stops 9 · placements google-search, chatgpt, perplexity, claude, mcp-registry, gmail + 4 catalog play:free-tools@1 (Not live yet, gaps #5) Every key from the catalog
Compiled, not written: edit the Play record free-tools 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.