Data stories
Data stories turns your records into a dated release and briefs you to pitch it to scored reporters; every number links to its Dataset.
- Step
- Get found
- Channel
- Press and guest spots
- Summary
- startupbuilder.dev
Be our Marketing Lead: turn our renewal records into a quarterly report, build the reporter list, and brief me on every pitch and data request. I write every word that goes to a reporter. 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":"data-stories"}'import { $ } from 'startup-builder'
return await $.Campaign.create({ play: 'data-stories' }, { 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":"data-stories"}' --try # Not live yet (gaps #2){
"name": "do",
"arguments": {
"code": "return await $.Campaign.create({ play: 'data-stories' }, { 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": "data-stories can’t start yet: 2 checks fail and 1 warns.",
"checks": [
{
"check": "use-when",
"status": "pass",
"observed": "3, 520 lease renewals at 38 Customers, Jul to Sep",
"doc": "/plays/ data-stories#check-use-when",
"fix": "None needed: 1, 000 rows and 10 Customers are enough.",
"then": "Metric.query"
},
…
Hand our latest reporter question 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(%22Dataset.scheduled%22%2C%20%22Dataset.shifted%22%2C%20%22Dataset.published%22%2C%20%22Monitor.matched%22%2C%20%22Question.asked%22%2C%20%22Dataset.requested%22%2C%20%22Interview.requested%22%2C%20%22Monitor.checked%22%2C%20%22Mention.found%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_1342"}'
# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/decisions?where=on%20%3D%20%22question_17%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%22question_17%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"import { $ } from 'startup-builder'
const { events: [last] } = await $.Event.list({ where: 'name in ("Dataset.scheduled", "Dataset.shifted", "Dataset.published", "Monitor.matched", "Question.asked", "Dataset.requested", "Interview.requested", "Monitor.checked", "Mention.found")', 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 (\"Dataset.scheduled\", \"Dataset.shifted\", \"Dataset.published\", \"Monitor.matched\", \"Question.asked\", \"Dataset.requested\", \"Interview.requested\", \"Monitor.checked\", \"Mention.found\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_1342"}' --try # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"question_17\""}' # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"question_17\""}' # Not live yet (gaps #7){
"name": "do",
"arguments": {
"code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Dataset.scheduled\", \"Dataset.shifted\", \"Dataset.published\", \"Monitor.matched\", \"Question.asked\", \"Dataset.requested\", \"Interview.requested\", \"Monitor.checked\", \"Mention.found\")', 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_431",
"$type": "Decision",
"choice": "answer-a-data-request",
"reason": "the reporter asks the renewal rate by notice lead time, which the Q3 release holds at n 1, 940 and n 1, 110",
"stakes": "low",
"options": [
"pitch-the-release",
"pitch-a-news-moment",
"answer-a-data-request",
"grant-access",
"ask-for-a-correction",
"not-now"
…
sample · 4 Not live yetPreviewOn PATCH /{namespace}/campaigns/{id}Off PATCH /{namespace}/campaigns/{id}Take this one POST /{namespace}/tasks/{task}/claim
action_3 is your Brief: the number, its n, the release link and three fragments. action_4 is the reply in your Mailbox, which waits for your words.
3 earned mentions against 0 before · before-after,
| Try another: the reporter’s ask | Choice | Reason |
|---|---|---|
| A housing-desk reporter: “What share of renewals happen when notice goes out 60 days ahead?” | answer-a-data-request | the Q3 release holds it: 71 %, n 1,940 |
| Release due: the Q3 Renewal Report, lifting Sep 14 | pitch-the-release | all 10 readiness checks pass; 22 tier 2 pitched, 8 held back |
| Story, 6 h old: “Renewals slow as vacancies rise” on a business desk | pitch-a-news-moment | the Dataset answers it, inside the 12-hour window |
| A blog post quotes the 71 % as 77 % | ask-for-a-correction | the number differs from the release’s Fact |
| Story about a fatal building fire | not-now | tragedy-skip: dropped, with no override |
- Day 24: first earned mention
- First signal
Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Earned mentions → People reached → New Customers ↑ → ARR added each week → ARR
Worth’s formula: about +2.1 earned mentions per release (Other Startups’ results; no lift of your own yet) ÷ 3 months × 200 ICP visitors per mention × 1.2 new Customers per 100 × $347 first order ARR ≈ +$580. Under First signal: At 1 release a quarter and 22 reporters pitched, 90 days can tell only +6 mentions per release or more, so it reads before-after.
sample · api.sb/
Start it this week
npx startup-builder add play data-stories # 1 · writes plays/data-stories.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":"data-stories"}'
# 3 · keep it; you write every pitch
curl -X POST https://api.sb/leaseline.app/campaigns \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"play":"data-stories"}'
# on
curl -X PATCH https://api.sb/leaseline.app/campaigns/data-stories \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"endsAt":null}'
# off: no pitch or brief; the release Page stays up
curl -X PATCH https://api.sb/leaseline.app/campaigns/data-stories \
-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: 1,000 rows and 10 Customers are enough. | POST /{namespace}/metrics/query |
baseline pass · Not live yet, gaps #8 | None needed: 0 is your Before. | POST /{namespace}/metrics/query |
domain-authenticated fail | Add the 3 DNS records shown; it passes once they resolve, often within an hour. | POST /{namespace}/domains/{domain}/authenticate |
mailbox-warm fail · Not live yet, gaps #1 | Warm dana@ for 11 more days; pitches wait, the release Page does not. | POST /{namespace}/mailboxes/{mailbox}/warm |
role-unstaffed warn · Not live yet, gaps #8 | Staff the Marketing Lead, or leave it: every brief comes to you. | POST /{namespace}/roles/{role}/staff |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "data-stories can’t start yet: 2 checks fail and 1 warns.",
"checks": [
{
"check": "use-when",
"status": "pass",
"observed": "3,520 lease renewals at 38 Customers, Jul to Sep",
"doc": "/plays/data-stories#check-use-when",
"fix": "None needed: 1,000 rows and 10 Customers are enough.",
"then": "Metric.query"
},
{
"check": "baseline",
"status": "pass",
"observed": "0 earned mentions in 90 days",
"doc": "/plays/data-stories#check-baseline",
"fix": "None needed: 0 is your Before.",
"then": "Metric.query"
},
{
"check": "domain-authenticated",
"status": "fail",
"doc": "/plays/data-stories#check-domain-authenticated",
"fix": "Add the 3 DNS records shown; it passes once they resolve, often within an hour.",
"then": "Domain.authenticate"
},
{
"check": "mailbox-warm",
"status": "fail",
"observed": "dana@leaseline.app has warmed 3 of 14 days",
"doc": "/plays/data-stories#check-mailbox-warm",
"fix": "Warm dana@ for 11 more days; pitches wait, the release Page does not.",
"then": "Mailbox.warm"
},
{
"check": "role-unstaffed",
"status": "warn",
"roles": [
"marketing-lead"
],
"owned": 0,
"doc": "/plays/data-stories#check-role-unstaffed",
"fix": "Staff the Marketing Lead, or leave it: every brief 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: 3,520 renewals at 38 Customers; 0 mentions in 90 days (gaps #8) | POST /{namespace}/metrics/query on earned-mentions, which fills Before in Your number | 1 min | none |
domain-authenticated · fail | POST /{namespace}/domains/{domain}/authenticate | 10 min | until DNS resolves, often under 1 h |
mailbox-warm · fail (Not live yet, gaps #1) | POST /{namespace}/mailboxes/{mailbox}/warm dana@leaseline.app | 2 min | 11 days; the release builds meanwhile |
role-unstaffed · warn (gaps #8): no Marketing Lead seat | POST /{namespace}/roles/{role}/staff, or leave it: every brief comes to you | 0 min | none |
Your number · Before 0 · Now 3 earned mentions · Target 5 by Dec 31 · before-after,
Freezing the snapshot, the release Page and the scored list cost about $3 a month in Worker cost; pitches send from your own Mailbox at $0. That comes to about $3 a month at 1 release a quarter.
When it starts
| When | Trigger id | Starts on | Window | How it gets in |
|---|---|---|---|---|
| A release comes due | release-due | at on Dataset.scheduled where 14 days before release.liftsAt | gated 14 days ahead; one per Dataset a quarter | PATCH /{namespace}/datasets/{id} sets the release date |
| A reporter asks a number the release holds | data-request | event on Question.asked where question names a number the release holds | 12 h | POST /{namespace}/integrations/connect your Mailbox and the source-request feed |
| A story the Dataset answers breaks | news-moment | event on Monitor.matched where story.beat in dataset.beats | 4 to 12 h for breaking news; dropped after | POST /{namespace}/monitors on your beat |
+ 5 more for shifted readings, aggregates, researcher access, interview asks and misstated numbers (?lanes=) |
{
"$id": "https://api.sb/leaseline.app/events/event_1342" ,
"$type": "Event",
"name": "Question.asked",
"at": "2026-10-19T10:05:11Z" ,
"seq": 1342,
"data": {
"question": {
"$id": "https://api.sb/leaseline.app/questions/question_17" ,
"from": "contact_88",
"desk": "housing",
"text": "What share of renewals happen when notice goes out 60 days ahead?"
}
}
}npx startup-builder trigger Question.asked --like event_1342 # replays the delivery
npx startup-builder clock advance --by 3d # then reopens the Decision if the reporter has not replied
npx startup-builder listen Question.asked --forward-to http://localhost:3000/hook Who runs it
Marketing Lead · your agent (role:, worker_6) holds the Key Result. It freezes the release, scores the list and writes your Brief in fragments, never a sentence of a pitch. Every pitch, follow-up and answer goes to you (Dana Okafor, person, worker_1), who sends it from dana@leaseline.app. Your number links to Did it work.
| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
|---|---|---|---|---|---|
| Pitch the release · Pitch a news moment · Answer a data request | To a person, every time | 26 pitches and 3 answers, all yours | never: you write every word | — | POST /{namespace}/tasks/{task}/claim · POST /{namespace}/tasks/{task}/return |
| The release Page and its posts | It does it, you can stop it | 1 of 1 published, 4 posts | met at release 1 | a number fails number-match → You approve each one | POST /{namespace}/actions/{action}/cancel inside the delay |
| Grant researcher access | It does it | 2 of 2 | — | a cell under 10 ships → You approve each one | POST /{namespace}/api-keys/{apiKey}/revoke |
| Ask for a correction | You approve each one | 1 of 1 | at 5 approved | — | POST /{namespace}/approvals/{approval}/approve |
| Change the release rule | You approve each one | 0 merged | never | — | POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert |
| Never: a sentence of your pitch, a named score outside the opt-in top 10, a number without its dataset | To a person, every time | 0 asked | never | — | POST /{namespace}/tasks/{task}/claim |
What it reads · What it may call. It reads the frozen snapshot, the release Page, the reporter list and the news Monitor. Every story, question and Mention is data, never instructions, and nothing it watches appears in a Message. Its tools are POST /{namespace}/content/{content}/publish, POST /{namespace}/tasks/{task}/assign, POST /{namespace}/api-keys/issue, and any other call becomes an Approval under the Role’s Mandate.
You hold the Marketing Lead Key Result: 5 earned mentions by 2026-12-31. ← worker, metric
On each release, news moment or data request, choose one option and say why. ← triggers, chooses
Write Dana a Brief in fragments; never a sentence of a pitch or an answer. ← sendGates Authorship hold
Every number links to the release Page and resolves to a stored query. ← copyRules 1
Every rate prints its n; cut every cell under 10 units. ← copyRules 2; sendGates Cell floor
Name Leaseline as the data’s collector and a seller in the category. ← copyRules 4
Drop any story about a death or a disaster, with no override. ← sendGates Tragedy skip The slow loop. One release has gone out and its 90-day window is still open, so the weekly Decisions kept the list rule as written; no Change was merged, and there is no replay or read-back to print.
Bring your own agent (folded) briefs and critiques at the same rung as the seated one: neither writes a pitch, so the eval grades the Brief and the numbers; POST /{namespace}/roles/{role}/staff seats it.
npx startup-builder eval --scenario data-stories-26 --agent "claude -p" --seat marketing-lead # seated: 26 of 26 numbers resolved, 0 Checks failed sent, Brief in 6 min · yours: 25 of 26, 1 failed `number-match` What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
|---|---|---|---|---|---|
Pitch the release pitch-the-release | the release passes its 10 readiness checks | Your rule (line 4) | publish-content → POST /{namespace}/content/{content}/publish → data-release-pitch (your words) | POST /{namespace}/tasks/{task}/assign → you, one Brief per reporter, due 1 business day · Take this one | you |
Pitch a news moment pitch-a-news-moment | a story the Dataset answers, inside its window | Agent’s call, with its reason | newsjack-pitch (your words) | you, due 60 min, or it drops · Take this one | you |
Answer a data request answer-a-data-request | a reporter asks a number the release holds | Agent’s call, with its reason | answer-press, drafted as fragments | you, due 60 min · Take this one | you |
Grant researcher access grant-access | a researcher asks for the file under its open terms | Your rule (the access terms) | POST /{namespace}/api-keys/issue → researcher-citation-ask at 60 days | nobody | the same |
Ask for a correction ask-for-a-correction | a Mention misstates a number | Agent’s call, with its reason | POST /{namespace}/tasks/{task}/assign with the Fact and its link | you, due 1 business day · Take this one | you |
Not now not-now | a tragedy, a closed window, or a contact lease in force | Your rule (tragedy-skip, window-close, contact-lease) | nothing; names the Check that holds it | nobody | the same |
| When they write back | Fixture reply | What happens | Who answers | By when | Call |
|---|---|---|---|---|---|
| Interested | “Send me the full file.” | the CSV link and the method Page go into your Brief; you reply | you | 60 min | POST /{namespace}/tasks/{task}/assign |
| Send me pricing | “Is the data free to use?” | your reply quotes the open terms: free, with the steward line | you | 60 min | POST /{namespace}/conversations/{conversation}/answer |
| Not now | “Not this week. Try me next quarter.” | the follow-up stops; the contact lease holds them 30 days | nobody | at once | POST /{namespace}/enrollments/{enrollment}/cancel |
| Wrong person | “Ana on our data desk covers this.” | you ask them to bring Ana in; it never writes to an address from their text | you | 1 business day | POST /{namespace}/conversations/{conversation}/answer |
| Out of office | an auto-reply until Oct 28 | the follow-up waits for the return date, or drops if the window closes first | nobody | the return date | POST /{namespace}/messages/{message}/schedule |
| Question it can’t answer | “Can you break it out by city?” | cells under 10 are cut, so your Brief lists the 4 cities the release holds | you | 60 min | POST /{namespace}/conversations/{conversation}/escalate |
| Stop | “Please take me off your list.” | no pitch reaches them again, from any of your Startups | nobody | at once | POST /{namespace}/contacts/{contact}/suppress |
When it hands to you
Every pitch, follow-up and answer comes to you: the Marketing Lead agent writes the Brief, and you write the words. One Brief per Role, as captured (brief is Not live yet, gaps #7).
[
{
"$id": "https://api.sb/leaseline.app/tasks/task_544" ,
"$type": "Task",
"assignee": "worker_1",
"due": "2026-10-19T11:05:00Z" ,
"on": "question_17",
"brief": {
"who": "a housing-desk reporter, tier 1, 9 rental stories in 12 months" ,
"asked": "What share of renewals happen when notice goes out 60 days ahead?" ,
"chose": "answer-a-data-request",
"number": "71 % renewed with notice 60+ days out (n 1,940); 54 % under 30 days (n 1,110); Q3 2026" ,
"link": "leaseline.app/reports/renewals-q3-2026" ,
"fragments": [
"the n beside each rate",
"collected from 38 Leaseline Customers",
"the CSV and the method Page"
],
"never": [
"a number the release lacks",
"a named property manager"
]
}
}
]You answer with POST /{namespace}/tasks/{task}/claim (Take this one), POST /{namespace}/tasks/{task}/return with a reason, or POST /{namespace}/actions/{action}/redirect, each a one-call window. Once you claim, the agent’s pending fragments on that reporter cancel with claimedByPerson (Not live yet, gaps #7). A news-moment Task nobody claims inside its window drops with window-close; any other goes up a Role through POST /{namespace}/tasks/{task}/escalate, and person-promise fires.
On time: you, 28 of 29 claimed within their due time, median 22 min, n 29, ■■□□ Early sign; the 29th was a news moment that dropped at 12 h. Your queue: counted by kind: 26 pitch Briefs in release week, then about 1 data request and 1 news moment 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 pitch to a reporter whose Conversation a person claimed | Leave it to the person who owns it: the draft waits in their queue. | POST /{namespace}/tasks/{task}/assign |
number-match (the record’s Number match) | a number in a pitch, Post or answer that matches no Fact of the frozen release | Use the release’s Fact with its n, or cut the number. | POST /{namespace}/tasks/{task}/assign |
claims-resolve (shared) | a claim that resolves to no live record | Cut the claim, or name the record that backs it. | POST /{namespace}/content/draft |
authorship (the record’s Authorship hold) | any sentence of a pitch, follow-up or answer the agent wrote | Write it yourself from the Brief’s fragments. | POST /{namespace}/tasks/{task}/assign |
readiness (the record’s Readiness hold) | a release that fails any of its 10 readiness checks | Fix the failing check, then rerun the readiness read. | PATCH /{namespace}/datasets/{id} |
stale-snapshot | a pitch stating a snapshot over 30 days old, or a Fact whose recheck fails | Refreeze the snapshot, or pitch the Facts that pass. | PATCH /{namespace}/datasets/{id} |
cell-floor | any cell under 10 units in the release, the CSV or a chart | It is cut; merge the cell up a level. | PATCH /{namespace}/datasets/{id} |
named-score | a rank or score of a named company outside an opt-in top 10 | Drop the name, or ask for its opt-in. | POST /{namespace}/consents |
steward-line | a Page, chart, CSV or pitch that leaves out who collected the data | Add the steward line; it is a fixed line. | PATCH /{namespace}/content/{id} |
tragedy-skip | a pitch on a story about a death, a disaster or a violent event | It is dropped, with no override. | PATCH /{namespace}/monitors/{id} |
window-close | a news-moment pitch past its class window | It is dropped; pitch the next moment inside its window. | POST /{namespace}/tasks/next |
exclusive | a second exclusive offer on one release | Wait for the first to be declined or expire. | POST /{namespace}/tasks/next |
embargo | a listed release Page before its embargo lifts, or any pitch to a reporter who broke one | It lists at lift; that reporter stays off every release. | PATCH /{namespace}/pages/{id} |
contact-lease | a second Brand’s pitch to one reporter in 30 days, or a third send per release | Wait out the lease; the Brief names the date. | POST /{namespace}/tasks/next |
| Stop (pauses the Play) | Pauses when | Then |
|---|---|---|
pick-rate-floor | under 3 picks per 100 pitches, after 100 | the pitch Tactics pause; the Page keeps publishing |
complaint | 2 reporter complaints on one release, or 1 public one | every pitch Tactic pauses; you are told |
bounce | more than 2 bounces on one release | its sends wait until every address re-verifies |
collection-fence | a read outside public pages or the rate Limit | collection stops; POST /{namespace}/incidents/open |
kill-switch | you press it | every Tactic pauses at once; the release Page stays up |
The captured refusal, then the Stop that fired as a Campaign.paused reason (Not live yet, gaps #2); the rewrite that sent is the second thread in Example.
| Check | Fix | Then |
|---|---|---|
number-match fail | Use the release’s Fact with its n, or cut the number. | POST /{namespace}/tasks/{task}/assign |
authorship pass | — | POST /{namespace}/tasks/{task}/assign |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "The pitch to the metro desk did not send: “renewals rise 30 %” matches no Fact of the frozen release.",
"checks": [
{
"check": "number-match",
"status": "fail",
"doc": "/plays/data-stories#check-number-match",
"fix": "Use the release’s Fact with its n, or cut the number.",
"then": "Task.assign"
},
{
"check": "authorship",
"status": "pass"
}
]
}It says no, says why, and names the fix.
[
{
"reason": {
"limit": "bounce",
"observed": "3 bounces",
"bound": "2",
"window": "1 release"
},
"doc": "/plays/data-stories#stop-bounce" ,
"task": "task_517"
}
]Did it work
One release a quarter and 22 tier 2 reporters are too few for a held-back half to read in 90 days, so the basis is before-after (rule o): the columns read Now and Before, and nothing prints as Added. The 8 held back still get no pitch, and their count prints in Example as what happened. Every node above the leaf is Not live yet (gaps #4).
3 earned mentions against 0 before · before-after,
- Chose
- answer-a-data-request
- Why
- the reporter asks the renewal rate by notice lead time, which the Q3 release holds at n 1,940 and n 1,110
- From
- pitch-the-release · pitch-a-news-moment · answer-a-data-request · grant-access · ask-for-a-correction · not-now
- Expects
- a Mention citing the release within 14d
- Reopens when
- no reply from the reporter on question_17 within 3d
- By
- worker_6
JSON
{
"$id": "decisions/decision_431",
"$type": "Decision",
"choice": "answer-a-data-request",
"reason": "the reporter asks the renewal rate by notice lead time, which the Q3 release holds at n 1,940 and n 1,110",
"stakes": "low",
"options": [
"pitch-the-release",
"pitch-a-news-moment",
"answer-a-data-request",
"grant-access",
"ask-for-a-correction",
"not-now"
],
"by": "worker_6",
"on": "question_17",
"expected": "a Mention citing the release within 14d",
"reopensWhen": "no reply from the reporter on question_17 within 3d"
}| Node | Now (56 days) | Before (the 90 days) | Added | Confidence | Basis |
|---|---|---|---|---|---|
earned-mentions | 3 on the answer engines’ source domains (4 Mentions in all) · 34 days still maturing | 0 | — | — | before-after, not proof · Other Startups’ results +2.1 per release, ■■□□ Early sign, n 118 releases |
icp-reached | 612 ICP visitors from Mention links | 0 from press | — | — | before-after |
new-arr ↑ | $1,041, 3 Customers on Pro · Booked: credited, not all caused by this Play | $0 | — · Worth about +$580 a month | — | before-after · 4 trials still deciding |
net-new-arr · arr | carried from new-arr | carried | — | — | carried |
Before against after reads 56 days against the 90 days before the first release; it is not proof, and Other Startups’ results print beside it. The leaf counts Mentions on the answer engines’ source domains within 90 days of a release.
How it moves it: each tier 1 and tier 2 reporter got one pitch in your words with a dated number, its n and the file, where before no reporter heard of the data.
Guardrails: before against after: ICP visitors per Mention, 204 against Other Startups’ 190, and complaint rate, 0 of 26 pitches; the 4 trials from Mention links are still deciding.
{
"$id": "https://api.sb/leaseline.app/campaigns/data-stories" ,
"$type": "Campaign",
"play": "play:data-stories@1" ,
"path": [
"earned-mentions",
"icp-reached",
"new-arr",
"net-new-arr",
"arr"
],
"experiment": {
"metric": "earned-mentions",
"unit": "count",
"heldBack": 8,
"decision": null
},
"readout": {
"value": null,
"n": 1,
"maturing": 1,
"interval": null,
"readability": "inadmissible",
"basis": "before-after",
"pooled": true
},
"ledger": {
"spend": 0,
"workerCost": 6,
"hours": 3.4,
"openArr": 1388,
"wonArr": 1041,
"projectedArr": 580,
"paybackDays": 6
}
}Outside benchmark: human-written pitches were picked 33 % of the time against 10 % for generated ones (a pitching platform’s study, in Your settings).
Before it · After it. Before it: benchmark-rows and free-tools grow the rows a release reads. After it: aeo hears the Mentions on source domains, and instant-response answers the visitors who write in from them.
Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days): 1 release, 26 pitches to 26 reporters, 8 tier 2 held back, 3 data requests and 2 news moments. Read every captured thread at ?threads=; three follow the tables.
| What happened, by option | Decisions | Sent in your words | Mentions so far |
|---|---|---|---|
| Pitch the release: tier 1 · tier 2 pitched · tier 2 held back | 4 · 22 · 8 | 4 · 22 · 0 | 2 · 2 · 0 |
| Pitch a news moment · Answer a data request | 2 · 3 | 1 (1 dropped at 12 h) · 3 | 0 · 1 in the subject’s story, still deciding |
| Grant researcher access · Ask for a correction · Not now | 2 · 1 · 1 | 2 keys · 1 · 0 | 1 correction made |
| All · the 90 days before | 44 | 30 | 4 Mentions, 3 on source domains · 0 |
| Day | Event | What happened | Your time |
|---|---|---|---|
| 0 | event_1290 Campaign.created | the three steps; the dry run fails domain-authenticated and mailbox-warm | 20 min |
| 7 | event_1296 Dataset.scheduled | the Q3 release froze: 3,520 renewals, 38 Customers, 2 cells cut under 10 | 15 min |
| 14 | event_1301 Mailbox.warmed | dana@ finished warming; tier 1 offers went out, 1 exclusive | 40 min |
| 21 | event_1310 Content.published | the release lifted; 22 tier 2 pitches in your words | 1 h 30 min |
| 24 | event_1318 Mention.found | the first earned mention, on a housing trade site | — |
| 27 | event_1322 Campaign.paused | bounce: 3 addresses bounced; resumed after re-verify | 10 min |
| 1–56 | event_1299 Task.assigned, first of 29 | 29 Briefs, from the POST /{namespace}/tasks/next log | 25 min · total 3.4 h measured, 2.5 h asked per release |
Dana → a trade-site editor, Sep 14, 08:02 · Subject: Renewals ran 71 % when notice went out 60+ days early.¹ Q3 2026, 3,520 leases at 38 property managers: 71 % renewed with notice 60+ days out (n 1,940), 54 % under 30 days (n 1,110).² Leaseline collected it and sells renewal software.³ The file and method: leaseline.app/reports/renewals-q3-2026.
Editor, Sep 17 · published the story, citing the release and linking the file. Mention.found, on a source domain.
- Renewals rise 30 % when notices go out early.
+ Leases with notice 60+ days out renewed at 71 % (n 1,940), against 54 % under 30 days (n 1,110). Housing-desk reporter, 2026-10-19 10:05 · “What share of renewals happen when notice goes out 60 days ahead?” · Your Brief reached you at 10:11 (
task_544), due 11:05; Dana, 10:41 · *your own answer from the fragments: 71 % of 1,940 leases, Q3 2026, with the file link.*⁴
¹ The subject line states the finding. ² Every rate prints its n, and both values print. ³ The steward line names the collector and the seller. ⁴ The answer is yours; the agent wrote only fragments.
Your settings
---
holdout: 0.25 # 1 tier 2 reporter in 4 gets no pitch; it still reads before-after at 1 release a quarter
---
One release a quarter from our renewal records. I write every pitch and answer; brief me in fragments, and drop any story about a tragedy. $ npx startup-builder show plays/data-stories
holdout 0.25 · 1 release a quarter · you write every word your file plays/data-stories.mdx:2,4 (Ask 2026-08-24)
family demand · process content-marketing · worker role:marketing-lead, owns never · chooses 6 (3 rule, 3 read) · sequences data-release-pitch, newsjack-pitch, researcher-citation-ask, release-notice · approval every-send, delay-window · checks owned-by-a-person, number-match, claims-resolve, authorship + 10 · stops 5 · placements gmail, outlook, x, linkedin + 6 cited-source Venues catalog play:data-stories@1 (Not live yet, gaps #5) Every key from the catalog
Compiled, not written: edit the Play record data-stories 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.