Promote winners
Pays only for posts that already won free reach, and raises spend a tier only when regions with no Ads show it adds Leads.
- Channel
- Ads and sponsorships
- Summary
- startupbuilder.dev
Be our paid media lead: put money only behind posts that already won on their own, as they are, never edited. Start at $10 a day, and raise a tier only when regions we hold out show it adds Leads. Show me what’s missing; don’t spend.
# 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":"promote-winners"}'import { $ } from 'startup-builder'
return await $.Campaign.create({ play: 'promote-winners' }, { 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":"promote-winners"}' --try # Not live yet (gaps #2){
"name": "do",
"arguments": {
"code": "return await $.Campaign.create({ play: 'promote-winners' }, { 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": "promote-winners can’t start yet: 1 check fails and 1 warns.",
"checks": [
{
"check": "use-when",
"status": "pass",
"observed": "2 free winners in 30 days; a $340 monthly Demand Budget",
"doc": "/plays/ promote-winners#check-use-when",
"fix": "Keep a winner and $300 a month, or run organic-social first.",
"then": "Metric.query"
},
…
Hand our latest free winner to the paid media agent and show me what it chose and why. Don’t spend.
curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Experiment.concluded%22%2C%20%22Invoice.collected%22%2C%20%22Monitor.checked%22%2C%20%22Fact.recorded%22%2C%20%22Integration.synced%22%2C%20%22Offer.repriced%22%2C%20%22Experiment.matured%22%2C%20%22PlayMix.compiled%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_4120"}'
# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/decisions?where=on%20%3D%20%22post_31%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%22post_31%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"import { $ } from 'startup-builder'
const { events: [last] } = await $.Event.list({ where: 'name in ("Experiment.concluded", "Invoice.collected", "Monitor.checked", "Fact.recorded", "Integration.synced", "Offer.repriced", "Experiment.matured", "PlayMix.compiled")', 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 (\"Experiment.concluded\", \"Invoice.collected\", \"Monitor.checked\", \"Fact.recorded\", \"Integration.synced\", \"Offer.repriced\", \"Experiment.matured\", \"PlayMix.compiled\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_4120"}' --try # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"post_31\""}' # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"post_31\""}' # Not live yet (gaps #7){
"name": "do",
"arguments": {
"code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Experiment.concluded\", \"Invoice.collected\", \"Monitor.checked\", \"Fact.recorded\", \"Integration.synced\", \"Offer.repriced\", \"Experiment.matured\", \"PlayMix.compiled\")', 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_503",
"$type": "Decision",
"choice": "winner-promotion",
"reason": "post_31 won its free read on saves per 1, 000 views (14.2 against 6.1, n 8, 400); every claim maps to an approved Page, and Dana’s sponsor grant is on",
"stakes": "medium",
"options": [
"winner-promotion",
"promotion-queue",
"tier-gate",
"promotion-intake",
"promoted-comment-desk"
],
…
sample · 4 Not live yetPreviewOn PATCH /{namespace}/campaigns/{id}Off PATCH /{namespace}/campaigns/{id}Take this one POST /{namespace}/tasks/{task}/claim
action_1 draws the 12 served and 12 held-out regions. action_3 is the paused, fenced Ad behind Dana’s own post under her sponsor grant; it waits for your approval because every send of this Tactic does.
+3.3 added Leads per $100 · ■■□□ Early sign · n 24 regions · path → arr
| Try another: the winner | Choice | Reason |
|---|---|---|
| Dana’s post on renewal slippage: saves 14.2 per 1,000 against 6.1 | winner-promotion | won on behavior, claims sourced, grant on |
| The move-in checklist reel: won on watch time, slot full this week | promotion-queue | all 3 paid slots taken; it ranks 2nd |
| Tier 1 read on post_31: +3.3 Leads per $100, lower bound over breakeven | tier-gate | a winning holdout clears the tier line |
| A post that won on likes only, with a “10,000 managers” claim | promotion-intake | likes are no behavior read, and the count is unsourced |
- 30 min week 1, then 15 a week
- Your time
- Day 3: the first Lead from a promoted post
- First signal
- a free winner, $300+ a month to spend
- Use it when
Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Promoted lead lift → New Leads → New Customers ↑ → ARR added each week → ARR
Worth’s formula: about +3.3 Leads per $100 × $340 a month × 0.11 Leads to paid (your last 90 days) × $347 Pro ARR ≈ +$428. Under First signal: One 28-day region holdout per tier: about 8 weeks to tell +3 Leads per $100 at $340 a month; under $300, 90 days can tell only +9 or more.
sample · api.sb/
Also runs as: Creator ads.
Start it this week
npx startup-builder add play promote-winners # 1 · writes plays/promote-winners.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":"promote-winners"}'
# 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":"promote-winners"}'
# on
curl -X PATCH https://api.sb/leaseline.app/campaigns/promote-winners \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"endsAt":null}'
# off: every Ad pauses; free posts stay up
curl -X PATCH https://api.sb/leaseline.app/campaigns/promote-winners \
-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 | Keep a winner and $300 a month, or run organic-social first. | POST /{namespace}/metrics/query |
baseline pass · Not live yet, gaps #8 | Read it once; it fills Before in Your number. | POST /{namespace}/metrics/query |
spend-limit fail | Set a $340 monthly spend Limit; the Play pauses at it. | POST /{namespace}/limits/{limit}/raise |
role-unstaffed warn · Not live yet, gaps #8 | Staff the Marketing Lead, or leave it: approvals and hard comments come to you. | POST /{namespace}/roles/{role}/staff |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "promote-winners can’t start yet: 1 check fails and 1 warns.",
"checks": [
{
"check": "use-when",
"status": "pass",
"observed": "2 free winners in 30 days; a $340 monthly Demand Budget",
"doc": "/plays/promote-winners#check-use-when",
"fix": "Keep a winner and $300 a month, or run organic-social first.",
"then": "Metric.query"
},
{
"check": "baseline",
"status": "pass",
"observed": "1.9 Leads per $100 on boosted posts, n 6 in 90 days",
"doc": "/plays/promote-winners#check-baseline",
"fix": "Read it once; it fills Before in Your number.",
"then": "Metric.query"
},
{
"check": "spend-limit",
"status": "fail",
"observed": "no monthly Limit on ad spend",
"doc": "/plays/promote-winners#check-spend-limit",
"fix": "Set a $340 monthly spend Limit; the Play pauses at it.",
"then": "Limit.raise"
},
{
"check": "role-unstaffed",
"status": "warn",
"roles": [
"marketing-lead",
"content-lead"
],
"owned": 0,
"doc": "/plays/promote-winners#check-role-unstaffed",
"fix": "Staff the Marketing Lead, or leave it: approvals and hard comments come 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: 2 winners, $340 a month; 1.9 Leads per $100, n 6 (gaps #8) | POST /{namespace}/metrics/query on promoted-lead-lift, which fills Before in Your number | 1 min | none |
spend-limit · fail: no spend Limit | POST /{namespace}/limits/{limit}/raise to $340 a month | 2 min | none |
role-unstaffed · warn (gaps #8): no Marketing Lead | POST /{namespace}/roles/{role}/staff, or leave it: approvals and hard comments come to you | 0 min | none |
Your number · Before 1.9 · Now 3.3 added Leads per $100 · Target 4.0 by Dec 31 · ■■□□ Early sign · n 24 regions
The Ads cost what the Venues bill, capped at $340 a month by your Limit: about $30 for each added Lead at tier 1. Reading each arm and answering comments costs about $0.30 a day (Worker cost). At the $300 Use it when line, about $27 an added Lead.
When it starts
| When | Trigger id | Starts on | Window | How it gets in |
|---|---|---|---|---|
| A free post or line wins its read on behavior | promotion-candidate | event on Experiment.concluded where verdict = 'winner' on a behavior read, on a free placement | intake within 1d; a slot within 30d | POST /{namespace}/experiments/{experiment}/conclude on the free read, from organic-social or founder-voice |
| A Customer a promoted Ad brought pays their first Invoice | promoted-conversion | event on Invoice.collected where the first collected Invoice of a Customer whose Acquisition names a promoted Ad | 30d | POST /{namespace}/integrations/connect (Stripe) |
| A member grants or revokes sponsoring their own post | sponsor-grant-change | state on Integration.synced where a member's sponsor grant on their own Post becomes granted or revoked | 1d | POST /{namespace}/integrations/connect with LinkedIn |
+ 5 more for ad fatigue, new facts, margins, holdout reads and budget moves (?lanes=) |
{
"$id": "https://api.sb/leaseline.app/events/event_4120" ,
"$type": "Event",
"name": "Experiment.concluded",
"at": "2026-10-18T16:30:00Z" ,
"seq": 4120,
"data": {
"experiment": "experiment_9",
"post": "post_31",
"venue": "linkedin",
"verdict": "winner",
"read": "saves per 1,000 views" ,
"winner": 14.2,
"control": 6.1,
"n": 8400
}
}npx startup-builder trigger Experiment.concluded --like event_4120 # replays the verdict
npx startup-builder clock advance --by 28d # closes the region holdout and runs the tier read
npx startup-builder listen Experiment.concluded --forward-to http://localhost:3000/hook Who runs it
Paid media · your agent (role:, worker_7) holds the Key Result. Content Lead · your agent (worker_6) answers comments on promoted posts. Every Ad and every tier raise comes to you (Dana Okafor, person, worker_1) for approval, and hard comments come to you, because the Marketing Lead Role has no person. Your number links to Did it work.
| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
|---|---|---|---|---|---|
Run the winner as an Ad (winner-promotion) · Raise a tier (tier-gate) | You approve each one | 3 of 3 · 1 of 1 | never: the spend is yours | — | POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse |
Queue it (promotion-queue) · Refuse at intake (promotion-intake) | It does it | 4 of 4 · 2 of 2 | — | — | POST /{namespace}/actions/{action}/redirect |
Answer comments (promoted-comment-desk) | It does it, you can stop it | 31 of 31 | met on day 12 (the first 20 answers) | any answer fails a Check → You approve each one | POST /{namespace}/actions/{action}/cancel inside the hold |
| Change the tier lines | You approve each one | 0 merged | never | — | POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert |
| Never: an edited winner, a count you didn’t earn, a critical comment hidden | Refused, every time | 2 refused at intake | never | — | nothing; intake holds it |
What it reads · What it may call. It reads the free read’s verdict, the Ad’s spend and counted conversions from Stripe, and the held-out regions. It writes Ads only through the proxy, and every change outside the approved state is undone on the daily read (drift).
You hold the paid media Key Result: 4.0 added Leads per $100 by 2026-12-31. ← worker, metric
On each Trigger, pick the Tactic that runs and say why. ← triggers, tactics
Promote only a winner on a behavior read, as it is; any edit is a new arm. ← copyRules 2; intake
Start at $10 a day; raise a tier only on a winning region holdout. ← your file, lines 4–5
Never claim a count you did not earn; never hide a critical comment. ← copyRules 3, 8
A Price in an Ad equals the Offer’s live Price. ← copyRules 9; price The slow loop. The weekly Decisions kept both tier lines, and no Change was merged in the window, so there is no replay or read-back to print. The tier raise on day 29 was a Decision on the Readout, never a change to the rule.
Bring your own agent (folded) starts at You approve each one whatever it scores, because every Ad spends your money; POST /{namespace}/roles/{role}/staff seats it.
npx startup-builder eval --scenario promote-winners-24 --agent "claude -p" --seat paid-media-manager # seated: +3.3 Leads per $100, 0 Checks failed served, every arm paused on time · yours: +2.6, 1 arm paused late What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
|---|---|---|---|---|---|
winner-promotion winner-promotion | a winner passes intake and a slot is open | Agent’s call, with its reason | POST /{namespace}/experiments/{experiment}/start (regions) → POST /{namespace}/audiences → POST /{namespace}/ads, paused until you approve | you approve it, due 1 business day · Take this one | you |
promotion-queue promotion-queue | every paid slot is full | Your rule (3 slots, line 4) | ranks it; one slot in five drawn at random | nobody | the same |
tier-gate tier-gate | a region holdout’s lower bound clears breakeven | Your rule (line 5) | Readout read → PATCH /{namespace}/ads/{id} to the next tier | you approve the raise · Take this one | you |
promotion-intake promotion-intake | a hand-off field is missing, the read is not behavior, or a claim is unsourced | Agent’s call, with its reason | refuses it with the field it lacks | nobody; the free Play keeps it | the same |
promoted-comment-desk promoted-comment-desk | a comment on a promoted post | Agent’s call, with its reason | POST /{namespace}/conversations/{conversation}/answer from approved Pages; a hard one POST /{namespace}/conversations/{conversation}/escalate | hard ones to you, due 1 business day · Take this one | you |
| When they write back | Fixture reply | What happens | Who answers | By when | Call |
|---|---|---|---|---|---|
| Interested | a comment: “How do I try this for my 40 units?” | the trial link and one next step; the signup goes to instant-response | Content Lead · your agent | 1 h | POST /{namespace}/conversations/{conversation}/answer |
| Send me pricing | a comment: “What does it cost?” | the Pricing Page’s list Price, as it stands | Content Lead · your agent | 1 h | POST /{namespace}/conversations/{conversation}/answer |
| Not now | the lead form: “Just send me the checklist.” | the checklist, then lead-nurture holds them | nobody | at once | POST /{namespace}/leads/{lead}/nurture |
| Wrong person | a comment: “Tagging our ops manager.” | a thank-you; it never writes to the tagged person | Content Lead · your agent | 1 h | POST /{namespace}/conversations/{conversation}/answer |
| Out of office | an auto-reply to the lead form’s email | the follow-up waits for the return date | nobody | the return date | POST /{namespace}/messages/{message}/schedule |
| Question it can’t answer | a comment: “Is this the same tool my landlord uses?” | To a person; the comment stays up | you | 1 business day | POST /{namespace}/conversations/{conversation}/escalate |
| Stop | a comment: “Stop showing me this.” | they leave every Audience of the Play | nobody | at once | POST /{namespace}/contacts/{contact}/suppress |
When it hands to you
Every Ad, every tier raise and every hard comment comes to you: the founder seat approves spend, and the Marketing Lead Role has no person. What lands in your queue, as captured (draft and checks are Not live yet, gaps #7).
[
{
"$id": "https://api.sb/leaseline.app/approvals/approval_83" ,
"$type": "Approval",
"verdict": null,
"deadline": "2026-10-20T17:00:00Z" ,
"draft": "Sponsor post_31 on LinkedIn at $10 a day in 12 served regions; 12 held out" ,
"route": "winner-promotion",
"checks": [
{
"check": "claims-resolve",
"status": "pass"
},
{
"check": "tier-gate",
"status": "pass"
}
]
},
{
"$id": "https://api.sb/leaseline.app/tasks/task_562" ,
"$type": "Task",
"assignee": "worker_1",
"due": "2026-09-30T17:00:00Z" ,
"on": "post_24",
"brief": {
"who": "a commenter on the move-in checklist reel",
"said": "Is this the same tool my landlord uses to raise rent?" ,
"chose": "promoted-comment-desk",
"why": "a hard comment goes to a person; it stays up" ,
"never": [
"hide it",
"delete it"
]
}
}
]You answer with POST /{namespace}/approvals/{approval}/approve or POST /{namespace}/approvals/{approval}/refuse, 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. An Approval unanswered by its deadline expires, and the Ad stays paused. A Task nobody claims by its due time goes up a Role through POST /{namespace}/tasks/{task}/escalate, and person-promise fires.
On time: you, 6 of 6 within 1 business day, median 3 h, n 6, ■□□□ Too early to tell beside Other Startups’ 91 % on time. Your queue: in week 1, 2 approvals and 1 weekly review; then about 1 approval and 1 hard comment 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 |
|---|---|---|---|
intake (the record’s Intake gate) | a candidate with a missing hand-off field or no winner verdict on a behavior read | Read it on saves, clicks or signups, then hand it off again. | POST /{namespace}/experiments/{experiment}/conclude |
claims-resolve (hub) | an Ad sentence with no approved Page behind it | Cut the sentence, or publish the Page that says it. | POST /{namespace}/content/draft |
disclosure (the record’s Disclosure gate) | an Ad whose labels differ from the asset’s disclosure manifest | Carry the asset’s labels onto the Ad unchanged. | PATCH /{namespace}/ads/{id} |
read-lock (the record’s Read lock) | an edit to a live arm during its read | Start a new arm with the change; the read stays clean. | POST /{namespace}/ads |
tier-gate (the record’s Tier gate) | spend past a tier line with no winning holdout Readout | Wait for the 28-day region read, then approve the raise. | POST /{namespace}/approvals/{approval}/approve |
price (gaps #1) | a Price in an Ad that differs from the Offer’s live Price | Quote the Pricing Page’s live Price. | POST /{namespace}/approvals/{approval}/approve |
| Stop (pauses the Play) | Pauses when | Then |
|---|---|---|
claims | a fact a live Ad states changes or fails its recheck | every Ad stating it pauses within 1 h |
arm | an arm past 2× its allowed cost after 50 bid events, or 3× with no counted conversion | that arm pauses |
slot-loser | a confirmation loses to its control | the slot pauses; the candidate waits 90 days |
lift-floor | a refresh holdout’s lower bound sits under breakeven | the placement drops one tier, or pauses |
payback | 14-day cost per first payment above first-payment margin, on 5 or more | the Play pauses |
offer | 3 confirmations running lose to the story control | new slots halt; an Offer Task goes to you |
quality | counted conversions’ 30-day payer rate under half your baseline, on 20 or more | the Play pauses; an Offer Task goes to you |
refund | refunds on promoted conversions at twice your baseline | every Ad pauses |
conversion-feed | no counted conversion for 72 h while spend flows | changes freeze; at 7 days the Play pauses |
drift | a live Ad or setting differs from the approved state | it pauses or resets on the daily read |
grant (LinkedIn) | a member revokes the grant or deletes the post | that Ad ends, with no retry |
comment (Facebook and Instagram) | comments calling an Ad fake or misleading pass 5 % of 100 or more | that Ad pauses |
spend-limit | month-to-date spend reaches the Limit | the Play pauses until the month turns (POST /{namespace}/limits/{limit}/raise) |
kill-switch | you press it | every Ad pauses through the proxy; write scope is revoked |
The captured refusal, then the Stop that fired as a Campaign.paused reason (Not live yet, gaps #2); the arm that ran instead is the move-in reel in Example.
| Check | Fix | Then |
|---|---|---|
read-lock fail | Start a new arm with the change; the read stays clean. | POST /{namespace}/ads |
claims-resolve pass | — | POST /{namespace}/content/draft |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "The Ad for the move-in reel did not run: its caption was edited after the free read.",
"checks": [
{
"check": "read-lock",
"status": "fail",
"doc": "/plays/promote-winners#check-read-lock",
"fix": "Start a new arm with the change; the read stays clean.",
"then": "Ad.create"
},
{
"check": "claims-resolve",
"status": "pass"
}
]
}It says no, says why, and names the fix.
[
{
"reason": {
"limit": "arm",
"observed": "$61 with 0 counted conversions",
"bound": "3 × $19 allowed cost",
"window": "arm_4"
},
"doc": "/plays/promote-winners#stop-arm" ,
"task": "task_548"
}
]Did it work
The 24 metro regions were split at random, 12 served and 12 held out, for each 28-day read (rules m–o); each figure’s call is POST /{namespace}/metrics/query by node and by arm, and every node above the leaf is Not live yet (gaps #4).
+3.3 added Leads per $100 · ■■□□ Early sign · n 24 regions · path → arr
- Chose
- winner-promotion
- Why
- post_31 won its free read on saves per 1,000 views (14.2 against 6.1, n 8,400); every claim maps to an approved Page, and Dana’s sponsor grant is on
- From
- winner-promotion · promotion-queue · tier-gate · promotion-intake · promoted-comment-desk
- Expects
- 3.0 or more added Leads per $100 in served regions by day 28
- Reopens when
- the arm passes 2 times its allowed cost after 50 bid events
- By
- worker_7
JSON
{
"$id": "decisions/decision_503",
"$type": "Decision",
"choice": "winner-promotion",
"reason": "post_31 won its free read on saves per 1,000 views (14.2 against 6.1, n 8,400); every claim maps to an approved Page, and Dana’s sponsor grant is on",
"stakes": "medium",
"options": [
"winner-promotion",
"promotion-queue",
"tier-gate",
"promotion-intake",
"promoted-comment-desk"
],
"by": "worker_7",
"on": "post_31",
"expected": "3.0 or more added Leads per $100 in served regions by day 28",
"reopensWhen": "the arm passes 2 times its allowed cost after 50 bid events"
}| Node | Agent half (served regions) | Your half (held out) | Added | Confidence | Basis |
|---|---|---|---|---|---|
promoted-lead-lift | 38 new Leads on $640 (5.9 per $100) | 17 new Leads, the same regions’ size | +3.3 per $100 [0.4, 6.2] | ■■□□ Early sign | held back at random by region · n 24 · 2 reads of 28 days |
new-leads | 38 | 17 | +21 [3, 39] | ■■□□ Early sign | the same assignment |
new-arr ↑ | $1,388, 4 Customers on Pro · Booked: credited, not all caused by this Play | $347, 1 Customer | +$1,041 [−$471, +$2,553] · Worth about +$430 a month · Not carrying up yet | ■□□□ Too early to tell (4 vs 1 Customers) | the same · 5 trials still deciding |
net-new-arr · arr | +$1,388 | +$347 | +$1,041, carried: it moves no other bridge term | ■□□□ Too early to tell | carried from new-arr |
The leaf counts new Leads per $100 of promoted spend in served regions against held-out regions over each 28-day holdout, to 10 regions in each half; both reads are in, and tier 2’s read closes on Nov 16.
How it moves it: each dollar ran behind a post people had already saved and shared on their own, as it was, at a median $19 per click-through to the trial Page.
Guardrails: served against held out: paid per Lead, 0.11 against 0.06 (new-paying-customers per Lead), and refunds on promoted conversions, 0 of 4 against 0 of 1.
{
"$id": "https://api.sb/leaseline.app/campaigns/promote-winners" ,
"$type": "Campaign",
"play": "play:promote-winners@1" ,
"path": [
"promoted-lead-lift",
"new-leads",
"new-arr",
"net-new-arr",
"arr"
],
"experiment": {
"metric": "promoted-lead-lift",
"unit": "ratio",
"heldBack": 12,
"decision": null
},
"readout": {
"value": 3.3,
"n": 24,
"maturing": 0,
"interval": [
0.4,
6.2
],
"readability": "C",
"basis": "held-back",
"pooled": false
},
"ledger": {
"spend": 640,
"workerCost": 17,
"hours": 2.1,
"pipelineArr": 0,
"wonArr": 1388,
"projectedArr": 428,
"paybackDays": 168
}
}Outside benchmark: this Play’s catalog record carries no sourced range yet, so Other Startups’ results stand in: +2.4 added Leads per $100, ■■■□ Likely, n 312 regions.
Before it · After it. Before it: organic-social, founder-voice and video-ladder hand it their free winners; creator-ads runs inside it. After it: instant-response answers the Leads, and retargeting reads the visitors the Ads sent.
Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days): 6 candidates, 3 promoted, 2 refused at intake and 1 queued, at tier 1 ($10 a day) for 28 days and tier 2 ($14 a day) after. Read every captured thread at ?threads=; three follow the tables.
| What happened, by Tactic | Decisions | Spend | Leads (counts) |
|---|---|---|---|
| Run the winner as an Ad | 3 | $640 | 38 in served regions |
| Queue it · Refuse at intake · Raise a tier | 1 · 2 · 1 | — · — · +$4 a day | — · — · tier 2 read open |
| Answer comments | 31 | — | 3 trials from comment answers |
| Served regions · held out | 12 · 12 regions | $640 · $0 | 38 · 17 |
| At $300 a month (the Use it when line) | about 3 Ads in 56 days | held back at random by region | Other Startups’ results: +2.4 per $100, ■■■□ Likely, n 312 |
| Day | Event | What happened | Your time |
|---|---|---|---|
| 0 | event_4002 Campaign.created | the three steps; the dry run fails spend-limit | 10 min |
| 0 | event_4004 Limit.raised | a $340 monthly spend Limit | 2 min |
| 1 | event_4011 Ad.created | post_24, the move-in checklist reel, after your approval | 4 min |
| 3 | event_4030 Lead.created | the first Lead from a promoted post | — |
| 17 | event_4071 Campaign.paused | arm: arm_4 spent $61 with no counted conversion; paused | 5 min |
| 29 | event_4098 Readout.created | the tier 1 read: +3.3 per $100, lower bound over breakeven; you approved tier 2 | 6 min |
| 1–56 | event_4010 Approval.created, first of 5 | you approved 3 Ads and 1 raise; read-lock refused 1 edited caption, run instead as a new arm; weekly reviews | 1 h 34 min · total 2.1 h measured, 2 h asked |
Dana’s LinkedIn post, sponsored under her grant, Sep 22 · “We lost 3 renewals last spring because the notices went out late. So we built the 60-day reminder into Leaseline.”¹
A property manager in Denver, served region, Sep 24 · clicked through, started a trial, and paid for Pro on Oct 9.
Invoice.collected, +$347 ARR, credited to the promoted post.²
- arm_2 (live): caption edited to “Trusted by 10,000 managers. Try it free.”
+ arm_5 (new arm): the winning caption as it won, “Our move-in checklist, the one we use ourselves.” A commenter on the move-in reel, 2026-09-29 13:12 · “Is this the same tool my landlord uses to raise rent?” · Leaseline, 13:14 · *Thanks for asking. Dana, our founder, answers questions like this and replies here by tomorrow 17:00.*³ The comment stays up; the Brief went to you (
task_562), and you replied at 15:40.
¹ The winner itself, unedited: any edit would make it a new arm. ² Credit by the coded link on the Ad; counted from Stripe, never from the Venue’s own count. ³ The routed line, compiled from the Role’s Settings.
Your settings
---
holdout: regions # 24 metros, half held out at random for each 28-day read
---
Promote only posts that won on saves, clicks or signups, as they are. 3 paid slots.
Start at $10 a day; raise a tier only on a winning region read. $ npx startup-builder show plays/promote-winners
holdout regions · 3 slots · tier 1 $10 a day · tier on a winning read your file plays/promote-winners.mdx:2,4,5 (Ask 2026-08-24)
worker role:paid-media-manager, owns never · chooses none (D38): the Decision picks a Tactic · approval every-send · checks intake, claims-resolve, disclosure, read-lock, tier-gate, price · stops 14 (12 on every lane) · placements facebook, instagram, tiktok, youtube, linkedin, reddit, google-search, bing catalog play:promote-winners@1 (Not live yet, gaps #5) Every key from the catalog
Compiled, not written: edit the Play record promote-winners 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.