Paid social
Runs 10 to 15 distinct ad concepts at a time, each paused until approved, and spends more only after a holdout shows added sales.
- Step
- Get found
- Channel
- Ads and sponsorships
- Summary
- startupbuilder.dev
Be our Marketing Lead for social ads: $1,000 a month on LinkedIn and Facebook, 12 distinct concepts a batch, each paused until I approve it. Judge them on trial starts, never clicks, and spend more only after a holdout shows they add sales. 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":"paid-social"}'import { $ } from 'startup-builder'
return await $.Campaign.create({ play: 'paid-social' }, { 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":"paid-social"}' --try # Not live yet (gaps #2){
"name": "do",
"arguments": {
"code": "return await $.Campaign.create({ play: 'paid-social' }, { 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": "paid-social can’t start yet: 1 check fails and 1 warns.",
"checks": [
{
"check": "use-when",
"status": "pass",
"observed": "$1, 000 a month; Pro’s margin $29 a month, $347 a year",
"doc": "/plays/ paid-social#check-use-when",
"fix": "Nothing to do: it needs $600 a month and a known margin.",
"then": "Metric.query"
},
…
Hand the latest ad set that is ready to judge to the Marketing Lead agent and show me what it chose and why. Don’t change anything.
curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Experiment.started%22%2C%20%22Metric.watched%22%2C%20%22Experiment.matured%22%2C%20%22Monitor.checked%22%2C%20%22Ad.rejected%22%2C%20%22Experiment.concluded%22%2C%20%22Conversation.closed%22%2C%20%22Audience.refreshed%22%2C%20%22Invoice.collected%22%2C%20%22Organization.announced%22%2C%20%22Offer.repriced%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_1410"}'
# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/decisions?where=on%20%3D%20%22experiment_9%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%22experiment_9%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"import { $ } from 'startup-builder'
const { events: [last] } = await $.Event.list({ where: 'name in ("Experiment.started", "Metric.watched", "Experiment.matured", "Monitor.checked", "Ad.rejected", "Experiment.concluded", "Conversation.closed", "Audience.refreshed", "Invoice.collected", "Organization.announced", "Offer.repriced")', 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.started\", \"Metric.watched\", \"Experiment.matured\", \"Monitor.checked\", \"Ad.rejected\", \"Experiment.concluded\", \"Conversation.closed\", \"Audience.refreshed\", \"Invoice.collected\", \"Organization.announced\", \"Offer.repriced\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_1410"}' --try # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"experiment_9\""}' # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"experiment_9\""}' # Not live yet (gaps #7){
"name": "do",
"arguments": {
"code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Experiment.started\", \"Metric.watched\", \"Experiment.matured\", \"Monitor.checked\", \"Ad.rejected\", \"Experiment.concluded\", \"Conversation.closed\", \"Audience.refreshed\", \"Invoice.collected\", \"Organization.announced\", \"Offer.repriced\")', 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_589",
"$type": "Decision",
"choice": "judge-the-concepts",
"reason": "batch 4 reached 54 trial starts in 7 days; concepts 3 and 8 beat the batch’s cost per trial by 40 % or more, and 7 concepts spent 3 times the target with none",
"stakes": "low",
"options": [
"refresh-the-concepts",
"judge-the-concepts",
"reset-the-drift",
"carry-an-angle",
"take-the-next-step",
"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_2 pauses the 7 losers through the proxy within 15 minutes, which you can stop. action_3 drafts 3 new concepts for the bottom quarter, paused until you approve them; no budget moves.
+16.7 per $100 · ■■□□ Early sign · n 8 weeks · path → arr
| Try another: the ad set | Choice | Reason |
|---|---|---|
| Batch 4 on LinkedIn: 54 trial starts in 7 days, 12 concepts | judge-the-concepts | enough events to judge: 2 winners, 7 losers |
| Batch 3 reached day 21 | refresh-the-concepts | the refresh date: retire the undecided, draft the bottom quarter |
| The live Facebook ad “60 days out” shows a caption no one approved | reset-the-drift | a Venue enhancement added it: reset at once |
| The renewal-timing Post won on LinkedIn organic: 4 times the median Contacts | carry-an-angle | an angle that won elsewhere: 2 paid concepts for your OK |
| The 8-week switchback: +16.7 per $100; breakeven at 12 | take-the-next-step | above breakeven, and the angle won twice: a raise to $1,500, with your OK |
- 2 h week 1, then 40 min a week
- Your time
- Day 5: first trial from a paid Visit
- First signal
- $600+ a month on one Venue, a known margin
- Use it when
Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Social ICP Visits per $100 → People reached → New Customers ↑ → ARR added each week → ARR
Worth’s formula: about +16.7 incremental ICP Visits per $100 (your switchback, ■■□□ Early sign) × $1,000 a month = 167 Visits × 0.02 Visits to paid × $347 Pro ARR ≈ +$1,159. Under First signal: An 8-week switchback of alternating weeks tells a +10 lift per $100 at $1,000 a month; each scale step waits for a 28-day holdout read.
sample · api.sb/
Start it this week
npx startup-builder add play paid-social # 1 · writes plays/paid-social.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":"paid-social"}'
# 3 · keep it: every concept waits for your OK
curl -X POST https://api.sb/leaseline.app/campaigns \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"play":"paid-social"}'
# the monthly Limit
curl -X POST https://api.sb/leaseline.app/limits \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"paid-social-month","amount":1000,"period":"month"}'
# off: every Campaign pauses through the proxy, and write scope is revoked
curl -X PATCH https://api.sb/leaseline.app/campaigns/paid-social \
-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 | Nothing to do: it needs $600 a month and a known margin. | POST /{namespace}/metrics/query |
baseline warn · Not live yet, gaps #8 | n 0: the switchback’s off-weeks become the baseline in 8 weeks. | POST /{namespace}/metrics/query |
billing-connected fail · Not live yet, gaps #8 | Connect Stripe, so every gate reads first sales from your own records. | POST /{namespace}/integrations/connect |
fact-check pass · Not live yet, gaps #1 | Nothing to do: every concept line must cite one of the 9. | POST /{namespace}/content/draft |
role-unstaffed pass · Not live yet, gaps #8 | Nothing to do: a founder-only Startup approves every concept. | POST /{namespace}/roles/{role}/staff |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "paid-social can’t start yet: 1 check fails and 1 warns.",
"checks": [
{
"check": "use-when",
"status": "pass",
"observed": "$1,000 a month; Pro’s margin $29 a month, $347 a year",
"doc": "/plays/paid-social#check-use-when",
"fix": "Nothing to do: it needs $600 a month and a known margin.",
"then": "Metric.query"
},
{
"check": "baseline",
"status": "warn",
"observed": "0 weeks of paid spend: no switchback yet",
"doc": "/plays/paid-social#check-baseline",
"fix": "n 0: the switchback’s off-weeks become the baseline in 8 weeks.",
"then": "Metric.query"
},
{
"check": "billing-connected",
"status": "fail",
"doc": "/plays/paid-social#check-billing-connected",
"fix": "Connect Stripe, so every gate reads first sales from your own records.",
"then": "Integration.connect"
},
{
"check": "fact-check",
"status": "pass",
"observed": "the claims source holds 9 claims",
"doc": "/plays/paid-social#check-fact-check",
"fix": "Nothing to do: every concept line must cite one of the 9.",
"then": "Content.draft"
},
{
"check": "role-unstaffed",
"status": "pass",
"observed": "every approval comes to you",
"doc": "/plays/paid-social#check-role-unstaffed",
"fix": "Nothing to do: a founder-only Startup approves every concept.",
"then": "Role.staff"
}
]
}It says no, says why, and names the fix.
| Check | Fix (one call) | Your time | Wait |
|---|---|---|---|
use-when · baseline · pass: $1,000 a month; warn: no switchback yet (gaps #8) | POST /{namespace}/metrics/query on social-icp-visits-per-100, which fills Before as the off-weeks run | 1 min | 8 weeks for the first read |
billing-connected · fail | POST /{namespace}/integrations/connect with system recurring-billing (Stripe) | 5 min | the first sync, about 10 min |
fact-check · pass: 9 claims (Not live yet) | nothing; it runs again on every concept | 0 min | none |
role-unstaffed · pass: 0 staffing fails on a founder-only Startup | nothing; every concept and step comes to you | 0 min | none |
Your number · Before 0 · Now 16.7 per $100 · Target 15 by Dec 31 · ■■□□ Early sign · n 8 weeks · Other Startups +8.2,
Media spend is $1,000 a month under its Limit; renders cost about $1.40 a concept under the render Limit, and the agent about $0.60 a day (Worker cost). That comes to about $1,035 a month at 12 concepts every 21 days.
When it starts
| When | Trigger id | Starts on | Window | How it gets in |
|---|---|---|---|---|
| An ad set reaches about 50 trial starts in 7 days, or runs out of time | verdict-ready | threshold on Experiment.matured where optimizationEvents >= 50 in 7d | judged within 1 h | POST /{namespace}/experiments/{experiment}/start set the ad set; the Venue reports through POST /{namespace}/integrations/connect |
| A concept batch reaches its 21st day | refresh-due | at on Experiment.started where batch.startedAt + 21d | the next batch within 7d | the batch date in your file |
| What runs on the Venue differs from what you approved | creative-drift | event on Monitor.checked where matchesApproved = false | reset within 1d | the daily POST /{namespace}/monitors/{monitor}/check of every live Ad |
+ 9 more: a tired winner, a rejected Ad, a won angle, new Customer words, a refreshed keeper list, a first sale, a rival’s moment, a margin change and a closed holdout (?lanes=) |
{
"$id": "https://api.sb/leaseline.app/events/event_1410" ,
"$type": "Event",
"name": "Experiment.matured",
"at": "2026-10-19T07:12:44Z" ,
"seq": 1410,
"data": {
"experiment": {
"$id": "https://api.sb/leaseline.app/experiments/experiment_9" ,
"venue": "linkedin",
"batch": 4,
"concepts": 12
},
"events": {
"kind": "trial-start",
"count": 54,
"days": 7
}
}
}npx startup-builder trigger Experiment.matured --like event_1410 # replays the delivery in test; the proxy writes nothing live
npx startup-builder clock advance --by 15m # pauses the 7 losers (action_2) unless you stop it
npx startup-builder listen Invoice.collected --forward-to http://localhost:3000/hook Who runs it
Marketing Lead · your agent (role:, worker_6) holds the Key Result and writes to the ads Venues only through the fenced proxy, which creates every Ad paused. You (Dana Okafor, person, worker_1) approve every concept, every carried angle and every spend step. Your number links to Did it work.
| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
|---|---|---|---|---|---|
| Judge the concepts · Reset what drifted | It does it, you can stop it | 4 verdicts · 2 resets | met on day 28 (the first 3 verdicts unedited) | a drift or fence-breach → You approve each one | POST /{namespace}/actions/{action}/cancel before the proxy writes |
| Refresh the concepts · Carry a won angle into ads | You approve each one | 41 of 48 concepts approved · 2 of 2 | never: a new concept is yours to approve | — | POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse |
| Take the next spend step | You approve each one | 1 asked, waiting on you | never: a spend step is yours | — | POST /{namespace}/approvals/{approval}/approve, then POST /{namespace}/limits/{limit}/raise |
| Not now | It does it | 2 of 2 | — | — | POST /{namespace}/actions/{action}/redirect |
| Change the concept rule | You approve each one | 1 merged of 1 | never | — | POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert |
| Never: a synthetic Customer, a face or voice with no yes, a fake play button | Refused, every time | 1 drafted, 1 refused | never | — | none: the Check holds it |
What it reads · What it may call. It reads trial starts, first sales from Stripe, spend and the approved sources, never click-through rate or the Venue’s own return on ad spend. Watched behavior never appears in Ad text, and tracking is added by the proxy at publish. Its tools are POST /{namespace}/content/draft, POST /{namespace}/ads, POST /{namespace}/approvals, POST /{namespace}/experiments/{experiment}/conclude, PATCH /{namespace}/ads/{id}, POST /{namespace}/limits/{limit}/raise, POST /{namespace}/experiments/{experiment}/start, and any other call becomes an Approval under the Role’s Mandate.
You hold the Marketing Lead Key Result: 15 incremental ICP Visits per $100 by 2026-12-31. ← worker, metric
On each verdict, refresh, drift or holdout, choose one option and say why. ← triggers, chooses
12 distinct concepts a batch, a quarter of them exploration. ← your file, line 5
No testimonial a real Customer didn’t give; no synthetic person speaks as a Customer. ← copyRules 3–4
Label every realistic synthetic person, voice or scene. ← copyRules 5
No spend step without a winning holdout read, an angle that won twice, and Dana’s OK. ← Scale gate, chooses.take-the-next-step The slow loop, as a Change. decision_405 (2026-09-21, weekly) read batches 1 and 2: 3 of 5 winners were product-screen concepts and 0 of 6 were stock-photo concepts. change_30 made at least half of each batch product-screen concepts (your file, line 6), and Dana merged it with POST /{namespace}/changes/{change}/merge. Replayed on past batches, POST /{namespace}/changes/{change}/review put winner share at 25 %; batch 4 read back at 17 % (2 of 12), n 12 concepts, ■□□□ Too early to tell (Example, day 56).
Bring your own agent (folded) starts at It does it, you can stop it when it matches or beats the seated agent on the eval suite, and at You approve each one when it does not; it still writes only through the proxy.
npx startup-builder eval --scenario paid-social-12 --agent "claude -p" --seat marketing-lead # seated: 17 % winner share, 0 Checks failed sent · yours: 8 %, 1 unlabeled render → You approve each one What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
|---|---|---|---|---|---|
Refresh the concepts refresh-the-concepts | the batch’s 21st day, or a tired winner | Your rule (line 5) | POST /{namespace}/content/draft 12 concepts → POST /{namespace}/ads, paused → POST /{namespace}/approvals | you approve the batch · Take this one | you |
Judge the concepts judge-the-concepts | about 50 trial starts in 7 days, or out of time | Your rule (the Concept stop) | POST /{namespace}/experiments/{experiment}/conclude → PATCH /{namespace}/ads/{id} to pause the losers | nobody | the same |
Reset what drifted reset-the-drift | a live Ad or setting differs from its approved source | Your rule (the Drift stop) | PATCH /{namespace}/ads/{id} back to the approved state | nobody | the same |
Carry a won angle into ads carry-an-angle | an angle won elsewhere, or Customers used new words | Agent’s call, with its reason | POST /{namespace}/content/draft 2 concepts from it → POST /{namespace}/approvals | you approve them · Take this one | you |
Take the next spend step take-the-next-step | a holdout read beats breakeven, and the angle won twice | Agent’s call, with its reason | POST /{namespace}/approvals → on yes, POST /{namespace}/limits/{limit}/raise → POST /{namespace}/experiments/{experiment}/start for the next 28-day read | you approve it · Take this one | you |
Not now not-now | no holdout read, or a read under breakeven | Agent’s call, with its reason | nothing; names what holds it: the open Experiment or holdout-floor | nobody | the same |
| When they write back | Fixture reply | What happens | Who answers | By when | Call |
|---|---|---|---|---|---|
| Interested | a lead form from the ad: “We manage 200 units. Demo?” | the form starts instant-response, which answers and books; this Play only counts the paid Visit | SDR · your agent, then AE · Priya Shah | 5 min | POST /{namespace}/leads |
| Send me pricing | a comment: “How much is it?” | one reply from the Company Page with Pro’s live Price and its link | Marketing Lead · your agent | 1 business day | POST /{namespace}/comments |
| Not now | a comment: “Not for us.” | nothing; no one is followed or retargeted from a comment | nobody | — | GET /{namespace}/decisions |
| Wrong person | a comment tagging a colleague | nothing writes to the person tagged | nobody | — | GET /{namespace}/decisions |
| Out of office | an off-week in the switchback | no Ad shows that week; it is the comparison | nobody | — | GET /{namespace}/experiments/{id} |
| Question it can’t answer | a comment: “Does it work for HOAs in Canada?” | To a person: a reply only you can give, with the true wait | you (Dana Okafor, person) | 1 business day | POST /{namespace}/tasks/{task}/assign |
| Stop | “Stop showing me this.”, through the Venue’s ad preferences | the Venue stops the Ad for them; 3 comments calling an Ad fake or AI-made pause it (comment) | nobody | at once | GET /{namespace}/decisions |
When it hands to you
Every concept batch, carried angle, spend step and unanswerable comment comes to you: the Marketing Lead Role has no person, and spend is never the agent’s call. One Brief, as captured, then the Approval (brief, draft and checks are Not live yet, gaps #7).
[
{
"$id": "https://api.sb/leaseline.app/tasks/task_469" ,
"$type": "Task",
"assignee": "worker_1",
"due": "2026-10-21T17:00:00Z" ,
"on": "experiment_8",
"brief": {
"what": "the 8-week switchback",
"read": "+16.7 incremental ICP Visits per $100 [+3.2, +30.2], breakeven 12" ,
"chose": "take-the-next-step",
"why": "above breakeven; the renewal-timing angle won on LinkedIn organic and in batch 3" ,
"step": "raise the Limit to $1,500 a month" ,
"never": [
"a raise without your OK"
]
}
},
{
"$id": "https://api.sb/leaseline.app/approvals/approval_99" ,
"$type": "Approval",
"verdict": null,
"deadline": "2026-10-21T17:00:00Z" ,
"draft": "3 concepts, paused: “Renewal notices, 60 days out, per owner” (product screen, 15 s)…" ,
"route": "refresh-the-concepts",
"checks": [
{
"check": "fact-check",
"status": "pass"
},
{
"check": "synthetic-media",
"status": "pass"
}
]
}
]You answer with POST /{namespace}/approvals/{approval}/approve or POST /{namespace}/approvals/{approval}/refuse, or POST /{namespace}/tasks/{task}/claim (Take this one) and POST /{namespace}/tasks/{task}/return with a reason, each a one-call window. An unapproved concept never goes live, and an unanswered spend step never happens; live Ads keep running at the current Limit.
On time: you, 5 of 5 concept batches approved within 2 days, median 5 h, n 5, ■■□□ Early sign beside Other Startups’ 90 % on time; the spend step of Oct 19 waits for you. Your queue: counted by kind: one concept batch every 21 days, about 1 carried angle a fortnight, and a spend step at each holdout read.
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 |
|---|---|---|---|
fact-check (Not live yet) | a concept line the claims source lacks, or a Price that differs from the live Price | Cut the claim, or name the record that backs it. | POST /{namespace}/content/draft |
synthetic-media (the synthetic media check Tactic) | an invented testimonial, a synthetic person speaking as a Customer, an unlabeled realistic render, or a face or voice with no yes | Use a real Customer’s words with their yes, or show the product instead. | POST /{namespace}/content/draft |
scale-gate (the record’s Scale gate) | a Limit raise or a new Venue with no winning holdout read and an angle that won twice | Run the 28-day holdout first; the angle must win twice. | POST /{namespace}/experiments/{experiment}/start |
holdout-floor (the record’s Holdout floor) | a raise when incremental return sits under the breakeven ratio | Spend holds at the last winning Limit; refresh the concepts instead. | POST /{namespace}/content/draft |
The captured refusal, then the Stop that fired as a Campaign.paused reason (Not live yet, gaps #2); the rewrite that went live is the batch 2 concept in Example.
| Check | Fix | Then |
|---|---|---|
synthetic-media fail | Use a real Customer’s words with their yes, or show the product instead. | POST /{namespace}/content/draft |
fact-check pass · Not live yet, gaps #1 | — | POST /{namespace}/content/draft |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "Concept 7 of batch 2 was not created: a synthetic person speaks as a Customer.",
"checks": [
{
"check": "synthetic-media",
"status": "fail",
"doc": "/plays/paid-social#check-synthetic-media",
"fix": "Use a real Customer’s words with their yes, or show the product instead.",
"then": "Content.draft"
},
{
"check": "fact-check",
"status": "pass"
}
]
}It says no, says why, and names the fix.
[
{
"reason": {
"limit": "comment",
"observed": "3 comments calling one Ad AI-made",
"bound": "3",
"window": "1 Ad, Oct 4"
},
"doc": "/plays/paid-social#stop-comment" ,
"task": "task_444"
}
]Did it work
Weeks were assigned at random, ads on or off (rule o: an 8-week switchback, 4 weeks a half, since Leaseline targets under 10 regions on LinkedIn); both halves are read from that assignment. Each figure’s call is POST /{namespace}/metrics/query by node and by arm; the parent field and every node above the leaf are Not live yet (gaps #4).
+16.7 per $100 · ■■□□ Early sign · n 8 weeks · path → arr
- Chose
- judge-the-concepts
- Why
- batch 4 reached 54 trial starts in 7 days; concepts 3 and 8 beat the batch’s cost per trial by 40 % or more, and 7 concepts spent 3 times the target with none
- From
- refresh-the-concepts · judge-the-concepts · reset-the-drift · carry-an-angle · take-the-next-step · not-now
- Expects
- cost per trial start down 20 % over the next 7 days
- Reopens when
- cost per trial start not down 20 % at the next Experiment.matured on experiment_9
- By
- worker_6
JSON
{
"$id": "decisions/decision_589",
"$type": "Decision",
"choice": "judge-the-concepts",
"reason": "batch 4 reached 54 trial starts in 7 days; concepts 3 and 8 beat the batch’s cost per trial by 40 % or more, and 7 concepts spent 3 times the target with none",
"stakes": "low",
"options": [
"refresh-the-concepts",
"judge-the-concepts",
"reset-the-drift",
"carry-an-angle",
"take-the-next-step",
"not-now"
],
"by": "worker_6",
"on": "experiment_9",
"expected": "cost per trial start down 20 % over the next 7 days",
"reopensWhen": "cost per trial start not down 20 % at the next Experiment.matured on experiment_9"
}| Node | Ads-on weeks | Ads-off weeks | Added | Confidence | Basis |
|---|---|---|---|---|---|
social-icp-visits-per-100 | 1,320 ICP Visits on $1,860 spend | 1,010 ICP Visits, $0 spend | +16.7 incremental ICP Visits per $100 [+3.2, +30.2] | ■■□□ Early sign | weeks switched at random, 4 a half · n 8 weeks · Other Startups +8.2, ■■■□ Likely, n 63 Startups |
icp-reached | 1,320 a half (330 a week) | 1,010 (253 a week) | +310 [+60, +560] | ■■□□ Early sign | the same assignment |
new-arr ↑ | $1,735, 5 Customers from paid Visits · Booked: credited, not all caused by this Play | $694, 2 Customers | +$1,041 [−$900, +$3,000] · Not carrying up yet · Worth about +$1,150 a month | ■□□□ Too early to tell (5 vs 2 Customers) | the same · 7 trials still deciding |
net-new-arr · arr | +$1,735 | +$694 | +$1,041, carried: it moves no other bridge term | ■□□□ Too early to tell | carried from new-arr |
The leaf counts ICP Visits in ads-on weeks minus ads-off weeks, for each $100 spent, over the 8-week switchback; a 28-day holdout comes before any raise and repeats every 90 days at steady spend.
How it moves it: 12 distinct concepts a batch reached people who fit your List, and 21 losers stopped spending after a median 6 days (social-icp-visits-per-100 by concept).
Guardrails: treated against held back: cost per first sale, $372 against a $347 margin, 14-day, under 5 first sales so the payback gate waits; and trials to paid, 5 of 19 against 2 of 7.
{
"$id": "https://api.sb/leaseline.app/campaigns/paid-social" ,
"$type": "Campaign",
"play": "play:paid-social@1" ,
"path": [
"social-icp-visits-per-100",
"icp-reached",
"new-arr",
"net-new-arr",
"arr"
],
"experiment": {
"metric": "social-icp-visits-per-100",
"unit": "per-100-usd",
"heldBack": 4,
"decision": null
},
"readout": {
"value": 16.7,
"n": 8,
"maturing": 0,
"interval": [
3.2,
30.2
],
"readability": "B",
"basis": "alternating-weeks",
"pooled": false
},
"ledger": {
"spend": 1860,
"workerCost": 34,
"hours": 6.8,
"pipelineArr": 0,
"wonArr": 1735,
"projectedArr": 1159,
"paybackDays": 390
}
}Outside benchmark: 5 % to 8 % of paid social concepts become winners (a creative-analytics vendor’s 2026 creative benchmark).
Before it · After it. Before it: positioning-tournament, organic-social and promote-winners hand it angles that already won. After it: lead forms start instant-response, retargeting hears the Visitors who left, and month-3 keepers refresh the list it looks like.
Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days): 4 ads-on weeks, $1,860 spent, 4 concept batches of 12, 48 concepts in all, 41 approved and 8 winners. Read every captured thread at ?threads=; three follow the tables.
| What happened, by option | Decisions | Done | Won (credited, not all caused by this Play) |
|---|---|---|---|
| Refresh the concepts · Judge the concepts | 4 · 4 | 48 drafted, 41 approved · 8 winners, 21 losers paused | 5 on Pro from paid Visits (+$1,735 ARR) |
| Reset what drifted · Carry a won angle into ads | 2 · 2 | 2 resets · 4 concepts, 2 winners | counted above |
| Take the next spend step · Not now | 1 · 2 | waiting on you · spend held twice | — |
| At $600 a month (the Use it when volume) | 10 concepts every 21 days | switchback: Too early to tell at 90 days | Other Startups’ results: +8.2 per $100, ■■■□ Likely, n 63 Startups |
| Day | Event | What happened | Your time |
|---|---|---|---|
| 0 | event_118 Campaign.created | the three steps; the dry run fails billing-connected | 30 min |
| 0 | event_122 Integration.connected | Stripe connected; the LinkedIn and Facebook accounts set up, enhancements off, all paused | 40 min |
| 5 | event_190 Offer.trialed | the first trial from a paid Visit, on batch 1’s product-screen concept | — |
| 28 | event_560 Role.updated | promoted: verdicts and drift resets apply unless you stop them | — |
| 28 · 56 | event_571 Change.merged · event_1001 Readout.created | half of each batch product-screen concepts: replayed at 25 % winners, batch 4 read back at 17 % | 5 min |
| 41 | event_702 Campaign.paused | comment: 3 comments called one Ad AI-made; it was a real screen recording, relabeled and resumed | 15 min |
| 56 | event_1003 Task.created | the switchback read +16.7 per $100: a raise to $1,500 asked of you | — |
| 1–56 | event_140 Approval.created, first of 6 | you approved 41 concepts in 5 batches; weekly reviews, from the POST /{namespace}/tasks/next log | 5 h 20 min · total 6.8 h measured, 7 h asked |
Batch 3, concept 4, LinkedIn, Sep 29 · a 15-second product screen: Renewal notices, 60 days out, per owner · Pro from $29 a month.¹
The click · Owen Hale’s colleague at a 90-unit firm landed on the renewals Page on Oct 2, started a trial at 11:40 and paid on Pro on Oct 14; Stripe recorded the first sale, and the conversion reached the Venue the next morning.²
- A smiling “property manager” on camera: “Leaseline saved my renewals.”
+ The renewal screen, then Rosa Lin’s words with her yes: “Every owner got the notice 60 days out.” A comment on batch 4’s Facebook Ad, 2026-10-12 09:30 · “Does it work for HOAs in Canada?” · Leaseline (agent), 09:31 · *Dana, our founder, answers this here by tomorrow 09:30.*³ The Brief went to Dana (
task_458); she replied at 16:15 the same day: yes for HOAs, and Canada from January.
¹ The Price equals Pro’s live Price, and every line cites a claim. ² Every gate counts the sale from Stripe, never the Venue’s own return. ³ The routed line, compiled from the Role’s Settings.
Your settings
---
limit: 1000 # dollars a month
venues: [linkedin, facebook]
---
12 distinct concepts a batch, every 21 days, a quarter of them exploration.
At least half of each batch shows the product on screen.
No raise without a holdout read, an angle that won twice, and my OK. $ npx startup-builder show plays/paid-social
limit $1,000 a month · venues linkedin, facebook · 12 concepts every 21 days · half product-screen · raises to you your file plays/paid-social.mdx:2,3,5,6,7 (Ask 2026-08-24; line 6 by change_30, 2026-09-21)
family ads · worker role:marketing-lead, owns never · chooses 6 (3 rule, 3 read) · approval every-send · checks fact-check, synthetic-media, scale-gate, holdout-floor · stops 15 · placements linkedin, facebook through the write proxy catalog play:paid-social@1 (Not live yet, gaps #5) Every key from the catalog
Compiled, not written: edit the Play record paid-social 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.