Skip to content

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.

Channel
Ads and sponsorships
Run oneMode try
You, to your agent

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

On 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 setChoiceReason
Batch 4 on LinkedIn: 54 trial starts in 7 days, 12 conceptsjudge-the-conceptsenough events to judge: 2 winners, 7 losers
Batch 3 reached day 21refresh-the-conceptsthe refresh date: retire the undecided, draft the bottom quarter
The live Facebook ad “60 days out” shows a caption no one approvedreset-the-drifta Venue enhancement added it: reset at once
The renewal-timing Post won on LinkedIn organic: 4 times the median Contactscarry-an-anglean angle that won elsewhere: 2 paid concepts for your OK
The 8-week switchback: +16.7 per $100; breakeven at 12take-the-next-stepabove breakeven, and the angle won twice: a raise to $1,500, with your OK

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/leaseline.app · 41 Customers at 2026-10-19 · on Experiment.matured · verdict 11 min after the ad set matured · 9 keys Not live yet

Start it this week

Terminal
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"}'
422Refusedpaid-social can’t start yet: 1 check fails and 1 warns.
CheckFixThen
use-when pass · Not live yet, gaps #8Nothing to do: it needs $600 a month and a known margin.POST /{namespace}/metrics/query
baseline warn · Not live yet, gaps #8n 0: the switchback’s off-weeks become the baseline in 8 weeks.POST /{namespace}/metrics/query
billing-connected fail · Not live yet, gaps #8Connect Stripe, so every gate reads first sales from your own records.POST /{namespace}/integrations/connect
fact-check pass · Not live yet, gaps #1Nothing to do: every concept line must cite one of the 9.POST /{namespace}/content/draft
role-unstaffed pass · Not live yet, gaps #8Nothing 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.

CheckFix (one call)Your timeWait
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 run1 min8 weeks for the first read
billing-connected · failPOST /{namespace}/integrations/connect with system recurring-billing (Stripe)5 minthe first sync, about 10 min
fact-check · pass: 9 claims (Not live yet)nothing; it runs again on every concept0 minnone
role-unstaffed · pass: 0 staffing fails on a founder-only Startupnothing; every concept and step comes to you0 minnone

Your number · Before 0 · Now 16.7 per $100 · Target 15 by Dec 31 · ■■□□ Early sign · n 8 weeks · Other Startups +8.2, ■■■□ Likely

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

WhenTrigger idStarts onWindowHow it gets in
An ad set reaches about 50 trial starts in 7 days, or runs out of timeverdict-readythreshold on Experiment.matured where optimizationEvents >= 50 in 7djudged within 1 hPOST /{namespace}/experiments/{experiment}/start set the ad set; the Venue reports through POST /{namespace}/integrations/connect
A concept batch reaches its 21st dayrefresh-dueat on Experiment.started where batch.startedAt + 21dthe next batch within 7dthe batch date in your file
What runs on the Venue differs from what you approvedcreative-driftevent on Monitor.checked where matchesApproved = falsereset within 1dthe 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=all)
One signed delivery · StartupBuilder-Signature
{
  "$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
    }
  }
}
Reproduce it in test
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:marketing-lead, 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.

OptionRung todayEvidence (passed of n)Moves up atDrops back whenYou can
Judge the concepts · Reset what driftedIt does it, you can stop it4 verdicts · 2 resetsmet on day 28 (the first 3 verdicts unedited)a drift or fence-breach → You approve each onePOST /{namespace}/actions/{action}/cancel before the proxy writes
Refresh the concepts · Carry a won angle into adsYou approve each one41 of 48 concepts approved · 2 of 2never: a new concept is yours to approve—POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse
Take the next spend stepYou approve each one1 asked, waiting on younever: a spend step is yours—POST /{namespace}/approvals/{approval}/approve, then POST /{namespace}/limits/{limit}/raise
Not nowIt does it2 of 2——POST /{namespace}/actions/{action}/redirect
Change the concept ruleYou approve each one1 merged of 1never—POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert
Never: a synthetic Customer, a face or voice with no yes, a fake play buttonRefused, every time1 drafted, 1 refusednever—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.

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

Terminal
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

OptionPicked whenDecided byRunsHands toIf it’s just you
Refresh the concepts refresh-the-conceptsthe batch’s 21st day, or a tired winnerYour rule (line 5)POST /{namespace}/content/draft 12 concepts → POST /{namespace}/ads, paused → POST /{namespace}/approvalsyou approve the batch · Take this oneyou
Judge the concepts judge-the-conceptsabout 50 trial starts in 7 days, or out of timeYour rule (the Concept stop)POST /{namespace}/experiments/{experiment}/conclude → PATCH /{namespace}/ads/{id} to pause the losersnobodythe same
Reset what drifted reset-the-drifta live Ad or setting differs from its approved sourceYour rule (the Drift stop)PATCH /{namespace}/ads/{id} back to the approved statenobodythe same
Carry a won angle into ads carry-an-anglean angle won elsewhere, or Customers used new wordsAgent’s call, with its reasonPOST /{namespace}/content/draft 2 concepts from it → POST /{namespace}/approvalsyou approve them · Take this oneyou
Take the next spend step take-the-next-stepa holdout read beats breakeven, and the angle won twiceAgent’s call, with its reasonPOST /{namespace}/approvals → on yes, POST /{namespace}/limits/{limit}/raise → POST /{namespace}/experiments/{experiment}/start for the next 28-day readyou approve it · Take this oneyou
Not now not-nowno holdout read, or a read under breakevenAgent’s call, with its reasonnothing; names what holds it: the open Experiment or holdout-floornobodythe same
When they write backFixture replyWhat happensWho answersBy whenCall
Interesteda 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 VisitSDR · your agent, then AE · Priya Shah5 minPOST /{namespace}/leads
Send me pricinga comment: “How much is it?”one reply from the Company Page with Pro’s live Price and its linkMarketing Lead · your agent1 business dayPOST /{namespace}/comments
Not nowa comment: “Not for us.”nothing; no one is followed or retargeted from a commentnobody—GET /{namespace}/decisions
Wrong persona comment tagging a colleaguenothing writes to the person taggednobody—GET /{namespace}/decisions
Out of officean off-week in the switchbackno Ad shows that week; it is the comparisonnobody—GET /{namespace}/experiments/{id}
Question it can’t answera comment: “Does it work for HOAs in Canada?”To a person: a reply only you can give, with the true waityou (Dana Okafor, person)1 business dayPOST /{namespace}/tasks/{task}/assign
Stop“Stop showing me this.”, through the Venue’s ad preferencesthe Venue stops the Ad for them; 3 comments calling an Ad fake or AI-made pause it (comment)nobodyat onceGET /{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).

What lands in each queue · sample
[
  {
    "$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)RefusesFixThen
fact-check (Not live yet)a concept line the claims source lacks, or a Price that differs from the live PriceCut 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 yesUse 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 twiceRun 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 ratioSpend holds at the last winning Limit; refresh the concepts instead.POST /{namespace}/content/draft
Stop (pauses the Play)Pauses whenThen
paybacka 14-day cost per first sale above first-sale margin, at 5 or more first salesthe Play pauses
zero-transaction14-day spend over 3 times the margin with no first salethe Play pauses
conceptan Ad spends 3 times the target cost per trial start with nonethat Ad pauses as a loser
spend-limitmonth-to-date spend reaches the monthly Limitthe Play pauses until the month turns
conversion-feedno conversion for 72 h while clicks flowbudget changes freeze; at 7 days the Play pauses
feed-mismatchthe Venue’s first sales differ from Stripe’s by over 30 % for 7 daysbudget changes freeze
drifta live Ad or setting differs from its approved sourceit pauses or resets at once; a second in 7 days pauses the ad set
enhancementa Venue enhancement found on without an opt-in Decisionit resets at once; a second in 7 days pauses that Venue
disclosurea live Ad with a realistic synthetic person, voice or scene and no labelit pauses at once; the Play drops to You approve each one
rejection2 Venue rejections in 7 days, or an account restrictionuploads pause on that Venue; a restriction pauses the Play there
comment3 comments on one Ad calling it fake, a scam or AI-madethat Ad pauses for your review
landinga landing Page fails its status or Price checkits Ads pause
render-limitrender spend reaches its Limitnew renders hold; live Ads keep running
fence-breach3 refused writes through the proxy in one daythe Play drops to read-only
kill-switchyou press itevery Campaign pauses through the proxy, and write scope is revoked

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.

422RefusedConcept 7 of batch 2 was not created: a synthetic person speaks as a Customer.
CheckFixThen
synthetic-media failUse 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.

The Stops that fired, as Campaign.paused reasons · sample
[
  {
    "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"
}
NodeAds-on weeksAds-off weeksAddedConfidenceBasis
social-icp-visits-per-1001,320 ICP Visits on $1,860 spend1,010 ICP Visits, $0 spend+16.7 incremental ICP Visits per $100 [+3.2, +30.2]■■□□ Early signweeks switched at random, 4 a half · n 8 weeks · Other Startups +8.2, ■■■□ Likely, n 63 Startups
icp-reached1,320 a half (330 a week)1,010 (253 a week)+310 [+60, +560]■■□□ Early signthe 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 tellcarried 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.

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

What happened, by optionDecisionsDoneWon (credited, not all caused by this Play)
Refresh the concepts · Judge the concepts4 · 448 drafted, 41 approved · 8 winners, 21 losers paused5 on Pro from paid Visits (+$1,735 ARR)
Reset what drifted · Carry a won angle into ads2 · 22 resets · 4 concepts, 2 winnerscounted above
Take the next spend step · Not now1 · 2waiting on you · spend held twice—
At $600 a month (the Use it when volume)10 concepts every 21 daysswitchback: Too early to tell at 90 daysOther Startups’ results: +8.2 per $100, ■■■□ Likely, n 63 Startups
DayEventWhat happenedYour time
0event_118 Campaign.createdthe three steps; the dry run fails billing-connected30 min
0event_122 Integration.connectedStripe connected; the LinkedIn and Facebook accounts set up, enhancements off, all paused40 min
5event_190 Offer.trialedthe first trial from a paid Visit, on batch 1’s product-screen concept—
28event_560 Role.updatedpromoted: verdicts and drift resets apply unless you stop them—
28 · 56event_571 Change.merged · event_1001 Readout.createdhalf of each batch product-screen concepts: replayed at 25 % winners, batch 4 read back at 17 %5 min
41event_702 Campaign.pausedcomment: 3 comments called one Ad AI-made; it was a real screen recording, relabeled and resumed15 min
56event_1003 Task.createdthe switchback read +16.7 per $100: a raise to $1,500 asked of you—
1–56event_140 Approval.created, first of 6you approved 41 concepts in 5 batches; weekly reviews, from the POST /{namespace}/tasks/next log5 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.²

Batch 2, concept 7, 2026-09-14 · synthetic-media refused, then the version that went live
- 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

plays/paid-social.mdx
---
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.
Text
$ 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

Key Id What it is
Tactic social-account-setup Social account setup prepares one ads Venue once: conversion send verified, creative enhancements off, one broad ad set per goal, exclusions synced, all paused. Starts at You approve each one.
Tactic concept-batch Concept batch builds 10 to 15 distinct concepts across angle, format and persona, a quarter of them exploration, checks them, and queues them paused. Starts at You approve each one.
Tactic synthetic-media-check Synthetic media check classes each render, labels each realistic synthetic person, voice or scene, and refuses invented testimonials and unconsented likenesses. Starts at Sends nothing.
Tactic concept-verdict Concept verdict marks each concept winner, loser or undecided once its ad set reaches about 50 optimization events in 7 days, with a downstream guard. Starts at Sends nothing.
Tactic creative-refresh Creative refresh retires undecided and tired Ads every 14 to 21 days, regenerates the bottom quarter, and plans the next batch at a quarter exploration. Starts at Sends nothing.
Tactic enhancement-opt-in Enhancement opt-in tries one Venue creative tool in a fenced ad set for 28 days, sampled daily, and keeps it if first Transactions say so. Starts at You approve each one.
Tactic ad-drift-monitor Ad drift monitor diffs every live Ad’s rendered creative, text and enhancement settings against the approved source daily, and pauses or resets what drifted. Starts at Sends nothing.
Tactic ad-write-proxy Ad write proxy is the only write path to an ads Venue, holding the token outside the model and forcing every create to paused. Starts at You approve each one.
Tactic incrementality-holdout Incrementality holdout measures the first Transactions a paid arm adds, by a geo holdout or a day switchback, before any scale step. Starts at You approve each one.
Tactic payback-gate Payback gate pauses a paid Play the moment its cost per first Transaction outruns first-transaction margin, with no approval and no wait. Starts at Sends nothing.
Trigger refresh-due Refresh due: at on Experiment.started, window 7d.
Trigger winner-fatigued Winner fatigued: threshold on Metric.watched, window 3d.
Trigger verdict-ready Verdict ready: threshold on Experiment.matured, window 7d.
Trigger creative-drift Creative drift: event on Monitor.checked, window 1d.
Trigger ad-rejected Ad rejected: event on Ad.rejected, window 1d.
Trigger angle-won Angle won: event on Experiment.concluded, window 30d.
Trigger customer-language-found Customer language found: event on Conversation.closed, window 30d.
Trigger keepers-refreshed Keepers refreshed: event on Audience.refreshed, window 7d.
Trigger paid-first-transaction Paid first transaction: event on Invoice.collected, window 30d.
Trigger rival-moment Rival moment: event on Organization.announced, window 14d.
Trigger margin-change Margin change: event on Offer.repriced, window 1d.
Trigger holdout-closed Holdout closed: event on Experiment.matured, window 7d.
Benchmark paid-social-winner-share Share of Ads that become winners in a creative dataset.: 5–8 % (A creative-analytics vendor’s 2026 creative benchmark., 2026-10-24).
Benchmark ai-creative-ctr-parity Click-through rate of AI-made Ads divided by human-made sibling Ads from the same advertiser, Campaign and day.: 1.17 ratio (A four-university field study on a native-ad network., 2026-01-29).

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.