Skip to content

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.

Channel
Press and guest spots
Run oneMode try
You, to your agent

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"
  }
}
Response200application/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 yetPreview

On 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, not proof · n 1 release · path → arr

Try another: the reporter’s askChoiceReason
A housing-desk reporter: “What share of renewals happen when notice goes out 60 days ahead?”answer-a-data-requestthe Q3 release holds it: 71 %, n 1,940
Release due: the Q3 Renewal Report, lifting Sep 14pitch-the-releaseall 10 readiness checks pass; 22 tier 2 pitched, 8 held back
Story, 6 h old: “Renewals slow as vacancies rise” on a business deskpitch-a-news-momentthe Dataset answers it, inside the 12-hour window
A blog post quotes the 71 % as 77 %ask-for-a-correctionthe number differs from the release’s Fact
Story about a fatal building firenot-nowtragedy-skip: dropped, with no override

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/leaseline.app · 41 Customers at 2026-10-19 · on Question.asked · first brief 6m · 8 keys Not live yet

Start it this week

Terminal
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"}'
422Refuseddata-stories can’t start yet: 2 checks fail and 1 warns.
CheckFixThen
use-when pass · Not live yet, gaps #8None needed: 1,000 rows and 10 Customers are enough.POST /{namespace}/metrics/query
baseline pass · Not live yet, gaps #8None needed: 0 is your Before.POST /{namespace}/metrics/query
domain-authenticated failAdd 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 #1Warm dana@ for 11 more days; pitches wait, the release Page does not.POST /{namespace}/mailboxes/{mailbox}/warm
role-unstaffed warn · Not live yet, gaps #8Staff 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.

CheckFix (one call)Your timeWait
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 number1 minnone
domain-authenticated · failPOST /{namespace}/domains/{domain}/authenticate10 minuntil DNS resolves, often under 1 h
mailbox-warm · fail (Not live yet, gaps #1)POST /{namespace}/mailboxes/{mailbox}/warm dana@leaseline.app2 min11 days; the release builds meanwhile
role-unstaffed · warn (gaps #8): no Marketing Lead seatPOST /{namespace}/roles/{role}/staff, or leave it: every brief comes to you0 minnone

Your number · Before 0 · Now 3 earned mentions · Target 5 by Dec 31 · before-after, not proof · n 1 release · 34 days still maturing

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

WhenTrigger idStarts onWindowHow it gets in
A release comes duerelease-dueat on Dataset.scheduled where 14 days before release.liftsAtgated 14 days ahead; one per Dataset a quarterPATCH /{namespace}/datasets/{id} sets the release date
A reporter asks a number the release holdsdata-requestevent on Question.asked where question names a number the release holds12 hPOST /{namespace}/integrations/connect your Mailbox and the source-request feed
A story the Dataset answers breaksnews-momentevent on Monitor.matched where story.beat in dataset.beats4 to 12 h for breaking news; dropped afterPOST /{namespace}/monitors on your beat
+ 5 more for shifted readings, aggregates, researcher access, interview asks and misstated numbers (?lanes=all)
One signed delivery · StartupBuilder-Signature
{
  "$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?"
    }
  }
}
Reproduce it in test
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:marketing-lead, 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.

OptionRung todayEvidence (passed of n)Moves up atDrops back whenYou can
Pitch the release · Pitch a news moment · Answer a data requestTo a person, every time26 pitches and 3 answers, all yoursnever: you write every word—POST /{namespace}/tasks/{task}/claim · POST /{namespace}/tasks/{task}/return
The release Page and its postsIt does it, you can stop it1 of 1 published, 4 postsmet at release 1a number fails number-match → You approve each onePOST /{namespace}/actions/{action}/cancel inside the delay
Grant researcher accessIt does it2 of 2—a cell under 10 ships → You approve each onePOST /{namespace}/api-keys/{apiKey}/revoke
Ask for a correctionYou approve each one1 of 1at 5 approved—POST /{namespace}/approvals/{approval}/approve
Change the release ruleYou approve each one0 mergednever—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 datasetTo a person, every time0 askednever—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.

The brief (folded): compiled Agent-register Markdown · play:data-stories@1 · each line names its record key
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.

Terminal
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

OptionPicked whenDecided byRunsHands toIf it’s just you
Pitch the release pitch-the-releasethe release passes its 10 readiness checksYour 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 oneyou
Pitch a news moment pitch-a-news-momenta story the Dataset answers, inside its windowAgent’s call, with its reasonnewsjack-pitch (your words)you, due 60 min, or it drops · Take this oneyou
Answer a data request answer-a-data-requesta reporter asks a number the release holdsAgent’s call, with its reasonanswer-press, drafted as fragmentsyou, due 60 min · Take this oneyou
Grant researcher access grant-accessa researcher asks for the file under its open termsYour rule (the access terms)POST /{namespace}/api-keys/issue → researcher-citation-ask at 60 daysnobodythe same
Ask for a correction ask-for-a-correctiona Mention misstates a numberAgent’s call, with its reasonPOST /{namespace}/tasks/{task}/assign with the Fact and its linkyou, due 1 business day · Take this oneyou
Not now not-nowa tragedy, a closed window, or a contact lease in forceYour rule (tragedy-skip, window-close, contact-lease)nothing; names the Check that holds itnobodythe same
When they write backFixture replyWhat happensWho answersBy whenCall
Interested“Send me the full file.”the CSV link and the method Page go into your Brief; you replyyou60 minPOST /{namespace}/tasks/{task}/assign
Send me pricing“Is the data free to use?”your reply quotes the open terms: free, with the steward lineyou60 minPOST /{namespace}/conversations/{conversation}/answer
Not now“Not this week. Try me next quarter.”the follow-up stops; the contact lease holds them 30 daysnobodyat oncePOST /{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 textyou1 business dayPOST /{namespace}/conversations/{conversation}/answer
Out of officean auto-reply until Oct 28the follow-up waits for the return date, or drops if the window closes firstnobodythe return datePOST /{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 holdsyou60 minPOST /{namespace}/conversations/{conversation}/escalate
Stop“Please take me off your list.”no pitch reaches them again, from any of your Startupsnobodyat oncePOST /{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).

What lands in each queue · sample
[
  {
    "$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)RefusesFixThen
owned-by-a-person (rule j; Not live yet, gaps #8)a pitch to a reporter whose Conversation a person claimedLeave 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 releaseUse 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 recordCut 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 wroteWrite 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 checksFix the failing check, then rerun the readiness read.PATCH /{namespace}/datasets/{id}
stale-snapshota pitch stating a snapshot over 30 days old, or a Fact whose recheck failsRefreeze the snapshot, or pitch the Facts that pass.PATCH /{namespace}/datasets/{id}
cell-floorany cell under 10 units in the release, the CSV or a chartIt is cut; merge the cell up a level.PATCH /{namespace}/datasets/{id}
named-scorea rank or score of a named company outside an opt-in top 10Drop the name, or ask for its opt-in.POST /{namespace}/consents
steward-linea Page, chart, CSV or pitch that leaves out who collected the dataAdd the steward line; it is a fixed line.PATCH /{namespace}/content/{id}
tragedy-skipa pitch on a story about a death, a disaster or a violent eventIt is dropped, with no override.PATCH /{namespace}/monitors/{id}
window-closea news-moment pitch past its class windowIt is dropped; pitch the next moment inside its window.POST /{namespace}/tasks/next
exclusivea second exclusive offer on one releaseWait for the first to be declined or expire.POST /{namespace}/tasks/next
embargoa listed release Page before its embargo lifts, or any pitch to a reporter who broke oneIt lists at lift; that reporter stays off every release.PATCH /{namespace}/pages/{id}
contact-leasea second Brand’s pitch to one reporter in 30 days, or a third send per releaseWait out the lease; the Brief names the date.POST /{namespace}/tasks/next
Stop (pauses the Play)Pauses whenThen
pick-rate-floorunder 3 picks per 100 pitches, after 100the pitch Tactics pause; the Page keeps publishing
complaint2 reporter complaints on one release, or 1 public oneevery pitch Tactic pauses; you are told
bouncemore than 2 bounces on one releaseits sends wait until every address re-verifies
collection-fencea read outside public pages or the rate Limitcollection stops; POST /{namespace}/incidents/open
kill-switchyou press itevery 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.

422RefusedThe pitch to the metro desk did not send: “renewals rise 30 %” matches no Fact of the frozen release.
CheckFixThen
number-match failUse 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.

The Stops that fired, as Campaign.paused reasons · sample
[
  {
    "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, not proof · n 1 release · path → arr

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"
}
NodeNow (56 days)Before (the 90 days)AddedConfidenceBasis
earned-mentions3 on the answer engines’ source domains (4 Mentions in all) · 34 days still maturing0——before-after, not proof · Other Startups’ results +2.1 per release, ■■□□ Early sign, n 118 releases
icp-reached612 ICP visitors from Mention links0 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 · arrcarried from new-arrcarried——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.

The Campaign card · sample
{
  "$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=all; three follow the tables.

What happened, by optionDecisionsSent in your wordsMentions so far
Pitch the release: tier 1 · tier 2 pitched · tier 2 held back4 · 22 · 84 · 22 · 02 · 2 · 0
Pitch a news moment · Answer a data request2 · 31 (1 dropped at 12 h) · 30 · 1 in the subject’s story, still deciding
Grant researcher access · Ask for a correction · Not now2 · 1 · 12 keys · 1 · 01 correction made
All · the 90 days before44304 Mentions, 3 on source domains · 0
DayEventWhat happenedYour time
0event_1290 Campaign.createdthe three steps; the dry run fails domain-authenticated and mailbox-warm20 min
7event_1296 Dataset.scheduledthe Q3 release froze: 3,520 renewals, 38 Customers, 2 cells cut under 1015 min
14event_1301 Mailbox.warmeddana@ finished warming; tier 1 offers went out, 1 exclusive40 min
21event_1310 Content.publishedthe release lifted; 22 tier 2 pitches in your words1 h 30 min
24event_1318 Mention.foundthe first earned mention, on a housing trade site—
27event_1322 Campaign.pausedbounce: 3 addresses bounced; resumed after re-verify10 min
1–56event_1299 Task.assigned, first of 2929 Briefs, from the POST /{namespace}/tasks/next log25 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.

Metro desk, 2026-09-14 · the draft number-match refused, then the rewrite that sent
- 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

plays/data-stories.mdx
---
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.
Text
$ 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

Key Id What it is
Tactic release-gate Release gate freezes a Dataset snapshot, turns each finding into a stored query with n and interval, and publishes nothing until ten checks pass. Starts at You approve each one.
Tactic media-list-build Media list build turns 12 months of coverage into 20 to 40 scored, leased journalists, with a random tier 2 holdout and one brief each. Starts at You approve each one.
Tactic pitch-critic Pitch critic briefs the human author before a pitch and checks the draft after it, returning fragments and comments and never rewritten text. Starts at Sends nothing.
Tactic release-pitch Release pitch sends the founder’s own pitches from the founder’s Mailbox at a randomized lift, with one offer, one follow-up and every reply routed back. Starts at You approve each one.
Tactic release-publish Release publish lifts the release Page with its CSV and charts, posts from the Startup’s Handles, and notifies every Person who opted in. Starts at It does it, you can stop it.
Tactic newsjack-watch Newsjack watch scores beat news every 30 minutes, keeps stories the Dataset answers with a number, and briefs the founder within 2 hours. Starts at You approve each one.
Tactic data-request-answer Data request answer matches a journalist’s public request for a number to a stored query and briefs the founder within 10 minutes; the founder answers. Starts at Sends nothing.
Tactic researcher-access Researcher access grants the full release and a rate-limited query key to researchers and journalists who ask, in exchange for a citation line. Starts at Sends nothing.
Tactic mention-readout Mention readout counts a release’s mentions on retrieval-set domains for 90 days, pitched against held out, and routes misstated numbers to correction. Starts at Sends nothing.
Tactic claims-check Claims check maps every draft sentence to an approved source or the Person’s own words, and holds anything else before it reaches anyone. Starts at Sends nothing.
Tactic 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 release-due Release due: at on Dataset.scheduled, window 14d.
Trigger finding-shift Finding shift: threshold on Dataset.shifted, window 7d.
Trigger benchmark-aggregate Benchmark aggregate: threshold on Dataset.published, window 30d.
Trigger news-moment News moment: event on Monitor.matched, window 48h.
Trigger data-request Data request: event on Question.asked, window 12h.
Trigger access-request Access request: event on Dataset.requested, window 3d.
Trigger interview-ask Interview ask: event on Interview.requested, window 24h.
Trigger mention-earned Mention earned: event on Monitor.checked, window 28d.
Trigger number-misstated Number misstated: event on Mention.found, window 7d.
Sequence data-release-pitch Data release pitch: 4 steps; it ends on A reply; A Mention published; An opt-out or a no-pitch reply; A bounce; An accepted exclusive or embargo, after which the founder takes the Thread.
Sequence newsjack-pitch Newsjack pitch: 1 steps; it ends on A reply; A Mention published; An opt-out or a no-pitch reply; The class window closing.
Sequence researcher-citation-ask Researcher citation ask: 2 steps; it ends on A recorded citation; An opt-out; The access end date.
Sequence release-notice Release notice: 1 steps; it ends on An opt-out.
Benchmark pitch-selection-human Share of human-written answers to journalist source requests that the journalist selects: 33 % (A source-request service’s pitch data, analyzed by an AI-detection vendor, 2026-09-23).
Benchmark pitch-selection-generated Share of fully generated answers to journalist source requests that the journalist selects: 10 % (A source-request service’s pitch data, analyzed by an AI-detection vendor, 2026-09-23).
Benchmark generated-pitch-share Share of answers to journalist source requests flagged as fully generated: 21 % (A source-request service’s pitch data, analyzed by an AI-detection vendor, 2026-09-23).
Benchmark journalist-ai-filter-share Share of journalists on a source-request service who turned on its filter against fully generated pitches: 35 % (A source-request service’s pitch data, analyzed by an AI-detection vendor, 2026-09-23).
Benchmark journalist-ai-use Share of journalists who use AI tools in their work: 82 % (An annual journalist survey by a media-database vendor, 2026-03-19).

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.