URL: /plays/free-tools · Id: `play:free-tools`

# 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.

Free tools ships only calculators an answer engine cannot replace, each as a [Page](https://startupbuilder.dev/business/nouns/page), an endpoint and an agent tool, and retires unused ones.

```text title="Set it up · Ask"
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.
```

```text title="Run one · Ask"
Hand our latest answer-engine check to the Marketing Lead agent and show me what it chose and why. Don’t send.
```

```ts title="Run one · SDK"
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 }
```

```json title="Run one · Response · sample"
{
  "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"
    ],
    "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"
  },
  "actions": [
    {
      "$id": "https://api.sb/leaseline.app/actions/action_1",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_6",
      "verb": "Monitor.check",
      "checks": []
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_2",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_6",
      "verb": "Tool.create",
      "checks": [
        {
          "check": "correctness",
          "status": "pass"
        }
      ]
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_3",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_6",
      "verb": "Page.create",
      "checks": [
        {
          "check": "claims-resolve",
          "status": "pass"
        }
      ]
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_4",
      "$type": "Action",
      "status": "pending",
      "reason": "awaitingApproval",
      "mode": "try",
      "by": "worker_6",
      "verb": "Approval.create",
      "checks": []
    }
  ]
}
```

`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 |

| Worth | Your time | Spend | First signal | Use it when | Instead |
| --- | --- | --- | --- | --- | --- |
| ≈ +$360 ARR a month at 300 users | 40 min week 1, then 10 a week | $0.002 per run, ~$3 a month at 1,400 runs | Day 6: first signup from a saved result | A live Product, 3+ computable jobs answer engines guess at | No paid next step: publish the Offer (<Op verb="Offer.publish" />) · no such job: `editorial-seo` |

**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/leaseline.app · 41 Customers at 2026-10-19 · on Monitor.checked · first tool live 3d 6h · 8 keys Not live yet`

## Start it this week
```bash
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"}'
```


```json title="The dry run’s 422 · 2026-08-24 · sample"
{
  "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"
    }
  ]
}
```

| Check | Fix (one call) | Your time | Wait |
| --- | --- | --- | --- |
| <a id="check-use-when"></a>`use-when` · <a id="check-baseline"></a>`baseline` · pass: 5 answer-proof jobs; 0.8 per 100 on your other organic Pages (gaps #8) | <Op verb="Metric.query" /> on `tool-signup-rate`, which fills Before in Your number | 1 min | none |
| <a id="check-approved-pages"></a>`approved-pages` · fail (Not live yet) | <Op verb="Site.publish" /> each tool’s method note | 10 min | none |
| <a id="check-role-unstaffed"></a>`role-unstaffed` · warn (gaps #8): no Marketing Lead seat | <Op verb="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 | <Op verb="Monitor.create" /> 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; <Op verb="Event.capture" /> records it |
| + 4 more for tool requests, source changes, computable support questions and day 90 (`?lanes=all`) | | | | |

```json title="One signed delivery · StartupBuilder-Signature"
{
  "$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
    }
  }
}
```

```bash title="Reproduce it in test"
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:marketing-lead`, `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:sales-lead`, `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 | — | <Op verb="Approval.approve" /> · <Op verb="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 | <Op verb="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 | <Op verb="Tool.update" /> |
| To the AE | It does it | 3 of 3 | — | — | <Op verb="Action.redirect" /> |
| Change the build rule | You approve each one | 0 merged | never | — | <Op verb="Change.merge" /> · <Op verb="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 | — | <Op verb="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 <Op verb="Tool.create" />, <Op verb="Page.create" />, <Op verb="Approval.create" />, <Op verb="Lead.create" />, <Op verb="Monitor.create" />, <Op verb="Content.publish" />, <Op verb="Task.assign" />, <Op verb="Decision.create" />, <Op verb="Tool.update" />, and any other call becomes an Approval under the Role’s Mandate.

```md title="The brief (folded): compiled Agent-register Markdown · play:free-tools@1 · each line names its record key"
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; <Op verb="Role.staff" /> seats it.

```bash
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 | <Op verb="Tool.create" /> with known answers → <Op verb="Page.create" /> → <Op verb="Approval.create" /> | 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) | <Op verb="Lead.create" /> → `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) | <Op verb="Monitor.create" /> → `tool-monitor-alerts` | nobody | the same |
| Publish the facts `publish-the-facts` | 30+ completed runs on a tool | Your rule (copy rule 1) | <Op verb="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 | <Op verb="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) | <Op verb="Decision.create" /> → <Op verb="Tool.update" /> | 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 | <Op verb="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 | <Op verb="Conversation.answer" /> |
| Not now | “Just the calculator, thanks.” | the follow-up stops; the tool stays free to use | nobody | at once | <Op verb="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 | <Op verb="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 | <Op verb="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 | <Op verb="Conversation.escalate" /> |
| Stop | “Stop these emails.” | emails and watch alerts stop, for all your Startups | nobody | at once | <Op verb="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).

```json title="What lands in each queue · sample"
[
  {
    "$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 <Op verb="Task.claim" /> (Take this one), <Op verb="Task.return" /> with a reason, <Op verb="Approval.approve" /> or <Op verb="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 <Op verb="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:** <Op verb="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 |
| --- | --- | --- | --- |
| <a id="check-owned-by-a-person"></a>`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. | <Op verb="Task.assign" /> |
| <a id="check-claims-resolve"></a>`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. | <Op verb="Content.draft" /> |
| <a id="check-abuse"></a>`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. | <Op verb="Task.claim" /> |
| <a id="check-public-fetch"></a>`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. | <Op verb="Tool.update" /> |

| Stop (pauses the Play) | Pauses when | Then |
| --- | --- | --- |
| <a id="stop-correctness"></a>`correctness` | a nightly known-answer case fails | the tool pauses on all three faces within 5 min |
| <a id="stop-freshness"></a>`freshness` | a live source older than its stated age | the tool pauses, or shows the source’s age where the method allows |
| <a id="stop-cost"></a>`cost` | a tool at its serving cap | cached results only; a raise is an Approval |
| <a id="stop-index"></a>`index` | over 30 % of a batch of aggregate Pages unindexed at 28 days | that batch is unpublished |
| <a id="stop-venue-notice"></a>`venue-notice` | a manual action or security notice on your Domain | all publishing pauses |
| <a id="stop-complaint"></a>`complaint` | complaints near 0.1 % of a week’s tool emails | the follow-up pauses on every Mailbox |
| <a id="stop-day-90-line"></a>`day-90-line` | a tool under 50 points at day 90 | it pauses 14 days, then retires |
| <a id="stop-signup-floor"></a>`signup-floor` | after 90 days and 200 users, a signup rate at or under your other organic Pages | new builds stop |
| <a id="stop-kill-switch"></a>`kill-switch` | you press it | every Tactic pauses; a second press takes every tool offline |

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.

```json title="The captured refusal · sample"
{
  "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"
    }
  ]
}
```


```json title="The Stops that fired, as Campaign.paused reasons · sample"
[
  {
    "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`

| 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.

```json title="The Campaign card · sample"
{
  "$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=all`; 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` <Op verb="Task.next" />, weekly | 4 tool approvals and 3 hand-offs, from the <Op verb="Task.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.

```diff title="The notice date tool Page, 2026-08-28 · the draft claims-resolve refused, then the rewrite that published"
- 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
```mdx title="plays/free-tools.mdx"
---
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.
```

```text
$ 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
| Key | Id | What it is |
| --- | --- | --- |
| Tactic | <a id="tactic-tool-gap-intake"></a>`tool-gap-intake` | Tool gap intake: Turns guessed answers, requests, support questions and source changes into a weekly list of answer-proof jobs that feed a paid Verb. Starts at You approve each one. |
| Tactic | <a id="tactic-tool-three-faces"></a>`tool-three-faces` | Three faces: Builds one Tool as a Page, a keyless JSON endpoint and an MCP tool under one name, tested, limited and described for agents. Starts at You approve each one. |
| Tactic | <a id="tactic-tool-aggregate-facts"></a>`tool-aggregate-facts` | Aggregate facts: Turns 30 or more completed runs into dated facts and one indexed aggregate Page per tool, released in batches. Starts at You approve the first wave. |
| Tactic | <a id="tactic-tool-capture-handoff"></a>`tool-capture-handoff` | Capture and handoff: Shows the full result first, turns each save, watch or send into a Lead, and hands each user and agent caller the paid next step. Starts at You approve the first wave. |
| Tactic | <a id="tactic-tool-portfolio-review"></a>`tool-portfolio-review` | Tool portfolio review: Scores every live tool weekly on use, signups, citations and agent calls, and retires those below the line at day 90 to a public graveyard. Starts at Sends nothing. |
| Tactic | <a id="tactic-send-gate-check"></a>`send-gate-check` | Send gate check: Checks Suppression, a live reason, the Market, the basis stamp, the Person-month grant, the facts and the labels before any cold Message exists. Starts at Sends nothing. |
| Trigger | <a id="trigger-answer-guess"></a>`answer-guess` | Answer guess: `event` on `Monitor.checked`, window 14d. |
| Trigger | <a id="trigger-tool-request"></a>`tool-request` | Tool request: `event` on `Issue.triaged`, window 2d. |
| Trigger | <a id="trigger-source-change"></a>`source-change` | Source change: `event` on `Monitor.checked`, window 2d. |
| Trigger | <a id="trigger-computable-question"></a>`computable-question` | Computable question: `event` on `Conversation.answered`, window 30d. |
| Trigger | <a id="trigger-repeat-tool-use"></a>`repeat-tool-use` | Repeat tool use: `threshold` on `Tool.called`, window 7d. |
| Trigger | <a id="trigger-tool-day-90"></a>`tool-day-90` | Tool day 90: `at` on `Version.deployed`, window 14d. |
| Sequence | <a id="sequence-tool-saved-result"></a>`tool-saved-result` | Tool saved result: 2 steps; it ends on Signup; Reply; Unsubscribe. |
| Sequence | <a id="sequence-tool-monitor-alerts"></a>`tool-monitor-alerts` | Tool monitor alerts: 2 steps; it ends on Unsubscribe; Monitor deleted; 90 days with no alert clicked; Tool retired, after one notice. |