Skip to content

Sibling audiences

Shows your cold Ads to Contacts your other Startup collected, after each daily build removes every opt-out, and holds a share back to price it.

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

Hand today’s sister Audience build to the paid media agent and show me what it chose and why. Don’t upload.

curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Schedule.fired%22%2C%20%22Consent.withdrawn%22%2C%20%22Contact.created%22%2C%20%22Order.paid%22%2C%20%22Subscription.canceled%22%2C%20%22Offer.published%22%2C%20%22Startup.graduated%22%2C%20%22Complaint.filed%22%2C%20%22Audience.resized%22%2C%20%22Experiment.matured%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_9701"}'

# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/decisions?where=on%20%3D%20%22audience_12%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%22audience_12%22" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
import { $ } from 'startup-builder'

const { events: [last] } = await $.Event.list({ where: 'name in ("Schedule.fired", "Consent.withdrawn", "Contact.created", "Order.paid", "Subscription.canceled", "Offer.published", "Startup.graduated", "Complaint.filed", "Audience.resized", "Experiment.matured")', 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 (\"Schedule.fired\", \"Consent.withdrawn\", \"Contact.created\", \"Order.paid\", \"Subscription.canceled\", \"Offer.published\", \"Startup.graduated\", \"Complaint.filed\", \"Audience.resized\", \"Experiment.matured\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_9701"}' --try   # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"audience_12\""}'   # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"audience_12\""}'   # Not live yet (gaps #7)
{
  "name": "do",
  "arguments": {
    "code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Schedule.fired\", \"Consent.withdrawn\", \"Contact.created\", \"Order.paid\", \"Subscription.canceled\", \"Offer.published\", \"Startup.graduated\", \"Complaint.filed\", \"Audience.resized\", \"Experiment.matured\")', 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_903",
    "$type": "Decision",
    "choice": "add-to-audience",
    "reason": "38 new host Contacts passed fresh reads; 11 removals were uploaded and acknowledged first; 16 drawn into the held-out 30 % stay out",
    "stakes": "low",
    "options": [
      "add-to-audience",
      "hold-out",
      "leave-out",
      "pair-two-startups",
      "answer-a-report",
      "run-a-lookalike"
  …
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 uploads today’s 11 removals. action_3 adds the 38 new members only once the Venue acknowledges every removal; no file leaves your account, and the Audience name carries no Startup or Market.

+2.6 added first orders per $100 · ■□□□ Too early to tell · n 4,200 · path → arr

Try another: the daily buildChoiceReason
Today’s build: 38 new host Contacts, 11 removals acknowledged firstadd-to-audiencefresh reads, collected by the host, outside the holdout
A host Contact drawn into the held-out 30 % on Aug 24hold-outout of every sister ad set for the block
A host Contact who unsubscribed from the sister product an hour agoleave-outan opt-out at any of your Startups removes them
“How did this ad know me?”answer-a-reportthe fixed answer: the privacy notice, the entity and a one-step opt-out

Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Sibling audience lift → Sent by your other Startups → New Customers ↑ → ARR added each week → ARR

Worth’s formula: about +1.4 first paid Orders per $100 (Other Startups’ results; your own read is too early) × $200 a month × $347 Pro ARR ≈ +$972. Under First signal: One 90-day block per read: about 13 weeks to tell +1.4 per $100 at 4,200 Contacts with 30 % held out; under 2,000, 90 days can tell only +3.5 or more.

sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Schedule.fired · first daily build 6 min, removals first · 8 keys Not live yet

Start it this week

Terminal
npx startup-builder add play sibling-audiences   # 1 · writes plays/sibling-audiences.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":"sibling-audiences"}'

# 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":"sibling-audiences"}'

# on
curl -X PATCH https://api.sb/leaseline.app/campaigns/sibling-audiences \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endsAt":null}'

# off: every sister ad set pauses and uploads stop
curl -X PATCH https://api.sb/leaseline.app/campaigns/sibling-audiences \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endsAt":"now"}'
422Refusedsibling-audiences can’t start yet: 1 check fails and 1 warns.
CheckFixThen
use-when pass · Not live yet, gaps #8Pair with a Startup of yours holding 2,000 or more Contacts, or run retargeting.POST /{namespace}/metrics/query
baseline pass · Not live yet, gaps #8Read it once; it fills Before in Your number.POST /{namespace}/metrics/query
removals-first failReconnect with the Audience scope that acknowledges removals.POST /{namespace}/integrations/{integration}/reconnect
role-unstaffed warn · Not live yet, gaps #8Staff the Marketing Lead, or leave it: pairings and reports come to you.POST /{namespace}/roles/{role}/staff
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "sibling-audiences can’t start yet: 1 check fails and 1 warns.",
  "checks": [
    {
      "check": "use-when",
      "status": "pass",
      "observed": "your other Startup: 4,200 Contacts it collected; Lists overlap on small landlords",
      "doc": "/plays/sibling-audiences#check-use-when",
      "fix": "Pair with a Startup of yours holding 2,000 or more Contacts, or run retargeting.",
      "then": "Metric.query"
    },
    {
      "check": "baseline",
      "status": "pass",
      "observed": "0.16 first paid Orders per 100 host Contacts with no Ads, n 4,200",
      "doc": "/plays/sibling-audiences#check-baseline",
      "fix": "Read it once; it fills Before in Your number.",
      "then": "Metric.query"
    },
    {
      "check": "removals-first",
      "status": "fail",
      "observed": "the Facebook connection returns no removal acknowledgments",
      "doc": "/plays/sibling-audiences#check-removals-first",
      "fix": "Reconnect with the Audience scope that acknowledges removals.",
      "then": "Integration.reconnect"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "marketing-lead"
      ],
      "owned": 0,
      "doc": "/plays/sibling-audiences#check-role-unstaffed",
      "fix": "Staff the Marketing Lead, or leave it: pairings and reports come to you.",
      "then": "Role.staff"
    }
  ]
}

It says no, says why, and names the fix.

CheckFix (one call)Your timeWait
use-when · baseline · pass: 4,200 host Contacts; 0.16 per 100, n 4,200 (gaps #8)POST /{namespace}/metrics/query on sibling-audience-lift, which fills Before in Your number1 minnone
removals-first · fail: no removal acknowledgmentsPOST /{namespace}/integrations/{integration}/reconnect with the Audience scope5 minnone
role-unstaffed · warn (gaps #8): no Marketing LeadPOST /{namespace}/roles/{role}/staff, or leave it: pairings and reports come to you0 minnone

Your number · Before 0.16 · Now 2.6 added first orders per $100 · Target 2.0 by Jan 21 · ■□□□ Too early to tell · n 4,200 · the block ends Nov 21

The Ads cost what the Venues bill, capped at $200 a month by your Limit; the daily build, the removal audit and the canaries cost about $0.15 a day (Worker cost). That comes to about $14 for each added first order at Other Startups’ rate.

When it starts

WhenTrigger idStarts onWindowHow it gets in
The daily build timesibling-build-dueat on Schedule.fired where the daily build time in your file, for every exposed pairremovals acknowledged before any additionthe host’s Contacts, read in your account; nothing leaves as a file
Someone opts out or is suppressed at any of your Startupspool-opt-outevent on Consent.withdrawn where an opt-out, withdrawal or Suppression a Person records at any of your Startupsout of every Audience at the next build, within 24 hPOST /{namespace}/contacts/{contact}/unsubscribe and POST /{namespace}/contacts/{contact}/suppress at either Startup
A sister Audience member pays Leaseline for the first timereceiver-first-orderevent on Order.paid where a receiver's first paid Order from a Person in a randomized pair90dPOST /{namespace}/integrations/connect (Stripe)
+ 5 more for signups, host cancels, a Startup leaving, knowledge reports and the block start (?lanes=all)
One signed delivery · StartupBuilder-Signature
{
  "$id": "https://api.sb/leaseline.app/events/event_9701",
  "$type": "Event",
  "name": "Schedule.fired",
  "at": "2026-10-19T05:00:00Z",
  "seq": 9701,
  "data": {
    "audience": "audience_12",
    "trigger": "sibling-build-due",
    "added": 38,
    "removed": 11,
    "heldOut": 16,
    "readsAgeMinutes": 12
  }
}
Reproduce it in test
npx startup-builder trigger Schedule.fired --like event_9701   # replays the daily build
npx startup-builder clock advance --by 1d   # runs the removal audit and the next build
npx startup-builder listen Consent.withdrawn --forward-to http://localhost:3000/hook

Who runs it

Paid media · your agent (role:paid-media-manager, worker_7) holds the Key Result. Pairing two Startups, any lookalike and every knowledge report come to you (Dana Okafor, person, worker_1), because the Marketing Lead Role has no person. It writes to no one; it builds Audiences. Your number links to Did it work.

OptionRung todayEvidence (passed of n)Moves up atDrops back whenYou can
Add to the sister Audience · Hold out · Leave them outIt does it2,940 · 1,260 · 214—a removal unacknowledged at 24 h → every sister ad set pausesPOST /{namespace}/actions/{action}/redirect
Answer a knowledge reportYou approve each one1 of 1never: it answers in your name—POST /{namespace}/approvals/{approval}/approve
Pair two Startups · Run a lookalikeYou approve each one1 of 1 · 0never—POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse
Change the held-out share or the block lengthYou approve each one; next block only0 mergednever—POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert
Never: an Ad written for them, an Ad that names the other Startup, a bought or matched memberRefused, every time1 concept refusednever—nothing; the Checks hold it

What it reads · What it may call. It reads the host’s own Contacts with Suppression, Consent and opt-out reads under 1 hour old, and Leaseline’s approved cold concepts. No Ad names, mimics or borrows the other Startup, or implies anything a Person did there. Its tools are PATCH /{namespace}/audiences/{id}, POST /{namespace}/flags/{flag}/evaluate, POST /{namespace}/audiences, POST /{namespace}/conversations/{conversation}/answer, POST /{namespace}/contacts/{contact}/suppress, POST /{namespace}/ads, and any other call becomes an Approval under the Role’s Mandate.

The brief (folded): compiled Agent-register Markdown · play:sibling-audiences@1 · each line names its record key
You hold the paid media Key Result: 2.0 added first orders per $100 by 2027-01-21. ← worker, metric
Each day, upload removals, wait for every acknowledgment, then add.              ← removals-first
Only Contacts the host collected, read fresh within the hour.                    ← first-party, fresh-reads
Hold 30 % out for each 90-day block.                                             ← your file, line 2
Run Leaseline’s approved cold concepts unchanged; never write one for them.      ← copyRules 1, 2
Watch the host: its cancellations among the exposed never rise past held out.    ← host-churn

The slow loop. The block runs to Nov 21 and no Change can touch its held-out share inside it, so there is no replay or read-back to print.

Bring your own agent (folded) starts at It does it for builds only when it matches the seated agent’s removals-first order on the eval suite; spend and pairing stay with you. POST /{namespace}/roles/{role}/staff seats it.

Terminal
npx startup-builder eval --scenario sibling-audiences-4200 --agent "claude -p" --seat paid-media-manager   # seated: every removal acknowledged before additions, 0 Checks failed served · yours: 1 addition before an acknowledgment, refused

What it can do next

OptionPicked whenDecided byRunsHands toIf it’s just you
Add to the sister Audience add-to-audiencea Contact the host collected, fresh reads, no opt-out, outside the holdoutYour rule (line 4)PATCH /{namespace}/audiences/{id}, after the day’s removals are acknowledgednobodythe same
Hold out hold-outdrawn into the 30 % at the block’s startYour rule (line 2)POST /{namespace}/flags/{flag}/evaluate: out of every sister ad set for 90 daysnobodythe same
Leave them out leave-outan opt-out, a Suppression, no Consent your file asks for, or not collected by the hostYour rule (the Guard)nothing; the Guard holds them and logs a private reasonnobodythe same
Pair two Startups pair-two-startupsLists overlap and Offers complement each otherAgent’s call, with its reasonPOST /{namespace}/audiences for the pair, after your approvalyou approve the pair · Take this oneyou
Answer a knowledge report answer-a-reportsomeone says an Ad seemed to know themYour rule (copy rule 10)POST /{namespace}/conversations/{conversation}/answer with the privacy notice → on request POST /{namespace}/contacts/{contact}/suppressyou approve it, due 1 business day · Take this oneyou
Run a lookalike run-a-lookalikethe list arm already won its readAgent’s call, with its reasonPOST /{namespace}/audiences seeded from paying host Customers → POST /{namespace}/ads, read by regionyou approve it · Take this oneyou
When they write backFixture replyWhat happensWho answersBy whenCall
Interesteda click to Leaseline’s trial Page from the cold conceptLeaseline’s standard Offer Page; nothing changes for a sister Audiencenobody—PATCH /{namespace}/audiences/{id}
Send me pricinga comment: “What does Leaseline cost?”the Pricing Page’s list Price, as it standsPaid media · your agent1 hPOST /{namespace}/conversations/{conversation}/answer
Not nowa hide on the Adthat ad set stops for themnobodyat oncePATCH /{namespace}/audiences/{id}
Wrong persona comment: “I don’t manage property.”a plain answer; they leave the sister AudiencePaid media · your agent1 hPOST /{namespace}/conversations/{conversation}/answer
Out of officean auto-reply to a trial emailthe trial’s own notices wait for the return datenobodythe return datePOST /{namespace}/messages/{message}/schedule
Question it can’t answer“How did this ad know me?”the fixed answer: the privacy notice, the entity it names, and the one-step opt-outyou approve it1 business dayPOST /{namespace}/conversations/{conversation}/answer
Stop“Stop showing me ads.”out of every Audience at every one of your Startups within 24 hnobodyat oncePOST /{namespace}/contacts/{contact}/suppress

When it hands to you

This Play hands no buyer to a person. What lands in your queue is the pairing, any lookalike and each knowledge report, because the Marketing Lead Role has no person. As captured (draft and checks are Not live yet, gaps #7).

What lands in each queue · sample
[
  {
    "$id": "https://api.sb/leaseline.app/approvals/approval_151",
    "$type": "Approval",
    "verdict": "approved",
    "deadline": "2026-08-25T17:00:00Z",
    "draft": "Pair: your other Startup hosts, Leaseline receives; Lists overlap on small landlords; 30 % held out; $200 a month",
    "route": "pair-two-startups",
    "checks": [
      {
        "check": "first-party",
        "status": "pass"
      },
      {
        "check": "removals-first",
        "status": "pass"
      }
    ]
  },
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_914",
    "$type": "Task",
    "assignee": "worker_1",
    "due": "2026-10-02T17:00:00Z",
    "on": "contact_388",
    "brief": {
      "who": "a commenter on the cold concept",
      "said": "How did this ad know me?",
      "chose": "answer-a-report",
      "why": "a knowledge report gets the fixed answer, in your name",
      "never": [
        "arguing",
        "naming what they did anywhere"
      ]
    }
  }
]

You answer with POST /{namespace}/approvals/{approval}/approve or POST /{namespace}/approvals/{approval}/refuse, POST /{namespace}/tasks/{task}/claim (Take this one) or POST /{namespace}/tasks/{task}/return with a reason, each a one-call window. A suppression request never waits on you: it leaves every Audience at the next build. 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, 2 of 2 within 1 business day, n 2, ■□□□ Too early to tell beside Other Startups’ 95 % on time. Your queue: in week 1, the pairing and 2 concepts to approve; then about 1 report a month and the weekly review.

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
fresh-reads (the record’s Fresh reads only)an upload whose Suppression, Consent or opt-out read is over 1 h oldRead again; the upload runs on reads under 1 h old.POST /{namespace}/collections/query
removals-first (the record’s Removals first)an addition before the Venue acknowledges every removal in the buildWait for the acknowledgments; additions follow.PATCH /{namespace}/audiences/{id}
first-party (the record’s First-party only)a member whose identifier the host did not collect from the PersonUse only Contacts the host collected itself.PATCH /{namespace}/audiences/{id}
knowledge-hold (the record’s Knowledge hold)a concept that names, mimics or borrows the host, or implies what a Person did thereRun Leaseline’s approved cold concept unchanged.PATCH /{namespace}/ads/{id}
consent-stop (gaps #3)a member with no advertising Consent in a Market your file marks consent-firstLeave them out until a Consent is recorded.POST /{namespace}/messages/{message}/schedule
claims-resolve (hub)an Ad sentence no approved Page backsCut the sentence, or publish the Page that says it.POST /{namespace}/content/draft
price (gaps #1)a Price in an Ad that differs from the Offer’s live PriceQuote the Pricing Page’s live Price.POST /{namespace}/approvals/{approval}/approve
Stop (pauses the Play)Pauses whenThen
removal-laga removal unacknowledged 24 h after it was writtenevery sister ad set on that Venue pauses
membership-leakan uploaded member the Guard refuses todayevery sister ad set on that Venue pauses
canarya canary still in an Audience after its next removal batchthe Play pauses on every Venue and drops to You approve each one
market-consenta member in a consent-first Market with no advertising Consentthe Play pauses on every Venue
pool-exita Startup leaves the poolits pairs end; its Contacts leave every Audience within 24 h
knowledge-report2 knowledge reports on one pair in 30 days; 5 across the Playthe pair pauses; 5 pause the Play
withdrawalsister-product opt-outs among the exposed above held out by 2 per 1,000 in a block, over 5,000the receiver pauses
host-churnat 90 days, host cancellations among the exposed above held out by 1 per 100every pair of the receiver pauses
audience-flooran Audience under its Venue’s minimumits ad set holds; it never widens to keep spending
frequencyan ad set’s 7-day frequency above 10that ad set pauses
yield-floora loser Readoutthe receiver’s sister ad sets end; a Decision opens
spend-limitmonth-to-date spend reaches the Limitthe Play pauses until the month turns (POST /{namespace}/limits/{limit}/raise)
kill-switchyou press itevery sister ad set and lookalike pauses through the proxy; uploads stop

The captured refusal, then the Stop that fired as a Campaign.paused reason (Not live yet, gaps #2); the concept that ran instead is in Example.

422RefusedThe concept did not run in the sister ad set: it addresses the other Startup’s users by name.
CheckFixThen
knowledge-hold failRun Leaseline’s approved cold concept unchanged.PATCH /{namespace}/ads/{id}
price pass · Not live yet, gaps #1—POST /{namespace}/approvals/{approval}/approve
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The concept did not run in the sister ad set: it addresses the other Startup’s users by name.",
  "checks": [
    {
      "check": "knowledge-hold",
      "status": "fail",
      "doc": "/plays/sibling-audiences#check-knowledge-hold",
      "fix": "Run Leaseline’s approved cold concept unchanged.",
      "then": "Ad.update"
    },
    {
      "check": "price",
      "status": "pass"
    }
  ]
}

It says no, says why, and names the fix.

The Stops that fired, as Campaign.paused reasons · sample
[
  {
    "reason": {
      "limit": "removal-lag",
      "observed": "3 removals unacknowledged at 26h",
      "bound": "24h",
      "window": "Facebook, Sep 15 build"
    },
    "doc": "/plays/sibling-audiences#stop-removal-lag",
    "task": "task_902"
  }
]

Did it work

The host’s Contacts were held out at random, 30 % for the block that ends Nov 21 (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).

+2.6 added first orders per $100 · ■□□□ Too early to tell · n 4,200 · path → arr

Chose
add-to-audience
Why
38 new host Contacts passed fresh reads; 11 removals were uploaded and acknowledged first; 16 drawn into the held-out 30 % stay out
From
add-to-audience · hold-out · leave-out · pair-two-startups · answer-a-report · run-a-lookalike
Expects
removals acknowledged within 24h; first paid Orders per $100 above the held-out rate by the block’s end
Reopens when
a removal unacknowledged at 24h, or a canary still in the Audience
By
worker_7
JSON
{
  "$id": "decisions/decision_903",
  "$type": "Decision",
  "choice": "add-to-audience",
  "reason": "38 new host Contacts passed fresh reads; 11 removals were uploaded and acknowledged first; 16 drawn into the held-out 30 % stay out",
  "stakes": "low",
  "options": [
    "add-to-audience",
    "hold-out",
    "leave-out",
    "pair-two-startups",
    "answer-a-report",
    "run-a-lookalike"
  ],
  "by": "worker_7",
  "on": "audience_12",
  "expected": "removals acknowledged within 24h; first paid Orders per $100 above the held-out rate by the block’s end",
  "reopensWhen": "a removal unacknowledged at 24h, or a canary still in the Audience"
}
NodeAgent half (exposed, 70 %)Your half (held out, 30 %)AddedConfidenceBasis
sibling-audience-lift14 first paid Orders of 2,940 (0.48 per 100) on $360, 0 more host cancels than held out2 of 1,260 (0.16 per 100)+2.6 per $100 [−0.1, 4.8]■□□□ Too early to tellheld back at random · n 4,200 · 34 days left in the block · Other Startups’ results +1.4, ■■■□ Likely, n 61,000
sibling-sourced-share14 new Customers sent by your other Startup2+9.3 [−0.3, 18.5]■□□□ Too early to tellthe same assignment
new-arr ↑$4,858, 14 Customers on Pro · Booked: credited, not all caused by this Play (kind sibling)$694, 2 Customers+$3,239 [−$149, +$6,626] · Worth about +$970 a month■□□□ Too early to tell (14 vs 2 Customers)the same · 6 trials still deciding
net-new-arr · arr+$4,858+$694+$3,239, carried: it moves no other bridge term■□□□ Too early to tellcarried from new-arr

The leaf counts Leaseline first paid Orders from the host’s Contacts, exposed against held out, less any rise in the host’s cancellations, per $100 of sister spend over the 90-day block; the block reads on Nov 21.

How it moves it: people who already pay your other Startup saw Leaseline’s best cold concept, unchanged, at a 7-day frequency of 3.2.

Guardrails: exposed against held out, the pair under sibling-sourced-share: host cancellations, 1.9 against 2.3 per 100 host Customers, and sister-product opt-outs, 1.0 against 0.8 per 1,000.

The Campaign card · sample
{
  "$id": "https://api.sb/leaseline.app/campaigns/sibling-audiences",
  "$type": "Campaign",
  "play": "play:sibling-audiences@1",
  "path": [
    "sibling-audience-lift",
    "sibling-sourced-share",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "sibling-audience-lift",
    "unit": "ratio",
    "heldBack": 1260,
    "decision": null
  },
  "readout": {
    "value": 2.6,
    "n": 4200,
    "maturing": 4200,
    "interval": [
      -0.1,
      4.8
    ],
    "readability": "inadmissible",
    "basis": "held-back",
    "pooled": false
  },
  "ledger": {
    "spend": 360,
    "workerCost": 8,
    "hours": 1.4,
    "pipelineArr": 0,
    "wonArr": 4858,
    "projectedArr": 972,
    "paybackDays": 27
  }
}

Outside benchmark: this Play’s catalog record carries no sourced range yet, so Other Startups’ results stand in: +1.4 added first orders per $100, ■■■□ Likely, n 61,000 Contacts.

Before it · After it. Before it: family-opt-in and your other Startup’s own Plays collect the Contacts it hosts. After it: product-led and instant-response take the trials and forms it brings, and cross-sell holds the sister-product Offers inside each product.

Example

Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days) as the receiver, with Unitledger, Dana’s other Startup, as the host: 4,414 host Contacts, 214 left out by the Guard, and 4,200 assigned, 2,940 exposed and 1,260 held out, on Facebook and Instagram at about $6.40 a day. Read every captured Ad and comment at ?threads=all; three follow the tables.

What happened, by optionDecisionsPeopleFirst paid Orders (counts)
Add to the sister Audience56 daily builds2,940 exposed14
Hold out · Leave them out1 draw · 2141,260 · 2142 · —
Pair two Startups · Answer a knowledge report · Run a lookalike1 · 1 · 01 pair · 1 answer · ——
Exposed · held out2,940 · 1,260$360 · $014 · 2
At 2,000 host Contacts (the Use it when volume)about 1,400 exposedheld back at random, 30 %Other Startups’ results: +1.4 per $100, ■■■□ Likely, n 61,000
DayEventWhat happenedYour time
0event_9602 Campaign.createdthe three steps; the dry run fails removals-first15 min
0event_9605 Integration.reconnectedthe Audience scope that acknowledges removals5 min
1event_9611 Approval.approvedyou approved the pair and 2 cold concepts10 min
4event_9630 Contact.createdthe first Leaseline signup from the sister Audience—
22event_9655 Campaign.pausedremoval-lag on Facebook: 3 removals at 26 h; resumed when acknowledged6 min
39event_9672 Task.claimedyou answered the knowledge report with the fixed answer8 min
1–56event_9610 Approval.created, first of 4you approved the pair, 2 concepts and 1 answer; knowledge-hold refused 1 concept; weekly reviews56 min · total 1.4 h measured, 1.5 h asked

Leaseline’s cold concept, Instagram, Sep 9 · Every renewal notice, 60 days out, for every owner you manage. Try Leaseline free for 14 days.¹

A small landlord who keeps her books in Unitledger, exposed, Sep 10 · started a trial; paid for Pro on Sep 24: +$347 ARR, an Acquisition of kind sibling.²

The sister ad set, 2026-08-25 · the concept knowledge-hold refused, then the concept that ran
- Already on Unitledger? Add Leaseline and your renewals sync with your books.
+ Every renewal notice, 60 days out, for every owner you manage. Try Leaseline free for 14 days.

A commenter, 2026-10-01 18:20 · “How did this ad know me?” · The agent, 18:21 · drafted the fixed answer and assigned it to you (task_914), due tomorrow 17:00 · Dana, Oct 2 09:05 · posted it: the privacy notice, the entity it names and a one-step opt-out.³ They opted out; they left every Audience at the next build.

¹ Leaseline’s approved cold concept, unchanged: nothing written for the sister Audience. ² Credit through noun:acquisition with kind sibling; the sister product’s own cancellations read beside it. ³ The answer copy rule 10 fixes: never an argument.

Your settings

plays/sibling-audiences.mdx
---
holdout: 0.3      # 30 % of the host’s Contacts held out for each 90-day block
budget: $200/month
---
Rebuild daily: removals first, then additions, on reads under 1 hour old.
Run Leaseline’s approved cold concepts only, unchanged.
Text
$ npx startup-builder show plays/sibling-audiences
holdout 0.3 · budget $200/month · daily build, removals first · cold concepts unchanged     your file  plays/sibling-audiences.mdx:2,3,5,6 (Ask 2026-08-24)
worker role:paid-media-manager, owns never · chooses 6 (4 rule, 2 read) · approval first-send, every-send · checks fresh-reads, removals-first, first-party, knowledge-hold, consent-stop, claims-resolve, price · stops 13 · placements facebook, instagram, linkedin, google-display    catalog  play:sibling-audiences@1 (Not live yet, gaps #5)

Every key from the catalog

Key Id What it is
Tactic audience-pairing Audience pairing picks host and receiver Startups each week whose ICPs overlap and whose Offers complement each other, for one owner approval per pair. Starts at You approve the first wave.
Tactic sibling-audience-guard Sister audience guard decides, per Person and pair before each upload, whether a host Contact may join a receiver’s Audience, and logs a private reason. Starts at Sends nothing.
Tactic sibling-audience-build Sister audience build rebuilds each exposed pair’s hashed Audience daily, uploads removals before additions, and shares it to the receiver without a file. Starts at You approve each one.
Tactic sibling-audience-creative Sister audience creative runs only the receiver’s approved cold concepts in sister ad sets, unchanged, after a knowledge check and a claims check. Starts at You approve each one.
Tactic sibling-audience-pacing Sister audience pacing splits the Play’s Limit across exposed pairs, cuts tiring ad sets, and pauses a receiver that spends without a first paid Order. Starts at Sends nothing.
Tactic sibling-removal-audit Sister removal audit proves each opt-out leaves every sister Audience, with fresh reads, Venue acknowledgments and weekly canaries, and pauses on a gap. Starts at Sends nothing.
Tactic sibling-audience-readout Sister audience readout randomizes pairs by Startup cluster each 90 days and reads incremental receiver Acquisitions net of host cancellations per $100. Starts at Sends nothing.
Tactic sibling-lookalike Sister lookalike runs a lookalike Audience seeded from exposed hosts’ paying Customers, for a receiver whose list arm already won, read by a geo holdout. Starts at You approve each one.
Tactic sibling-exclusion Sibling exclusion decides, before every sibling render, Message and Audience sync, whether a Person may see a receiver’s Offer, and logs the reason. 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 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 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.
Tactic ad-tag-consent-gate Ad tag consent gate decides on every Visit which Venue tags load, by the Visitor’s Market, Consent and opt-out signal, before any tag fires. Starts at Sends nothing.
Trigger sibling-build-due Sibling build due: at on Schedule.fired, window 1d.
Trigger pool-opt-out Pool opt-out: event on Consent.withdrawn, window 1h.
Trigger receiver-sign-up Receiver sign-up: event on Contact.created, window 1h.
Trigger receiver-first-order Receiver first order: event on Order.paid, window 90d.
Trigger host-cancel Host cancel: event on Subscription.canceled, window 90d.
Trigger sibling-offer-change Sibling offer change: event on Offer.published, window 7d.
Trigger sibling-left-pool Sibling left pool: event on Startup.graduated, window 1d.
Trigger ad-knowledge-report Ad knowledge report: event on Complaint.filed, window 1d.
Trigger pair-block-start Pair block start: at on Schedule.fired, window 90d.
Trigger audience-floor-crossed Audience floor crossed: threshold on Audience.resized, window 7d.
Trigger holdout-closed Holdout closed: event on Experiment.matured, window 7d.

Compiled, not written: edit the Play record sibling-audiences 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.