Skip to content

Family opt-in

Family opt-in shows one unticked box on your other Startup’s sign-up, asking for your updates, and records each yes as its own Consent.

Channel
Newsletter
Run oneMode try
You, to your agent

Hand our latest family yes to the Marketing Lead agent and show me what it chose and why. Don’t send.

curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Form.opened%22%2C%20%22Contact.subscribed%22%2C%20%22Consent.withdrawn%22%2C%20%22Offer.published%22%2C%20%22Brand.lapsed%22%2C%20%22Portfolio.changed%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_1702"}'

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

const { events: [last] } = await $.Event.list({ where: 'name in ("Form.opened", "Contact.subscribed", "Consent.withdrawn", "Offer.published", "Brand.lapsed", "Portfolio.changed")', 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 (\"Form.opened\", \"Contact.subscribed\", \"Consent.withdrawn\", \"Offer.published\", \"Brand.lapsed\", \"Portfolio.changed\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_1702"}' --try   # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"contact_77\""}'   # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"contact_77\""}'   # Not live yet (gaps #7)
{
  "name": "do",
  "arguments": {
    "code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Form.opened\", \"Contact.subscribed\", \"Consent.withdrawn\", \"Offer.published\", \"Brand.lapsed\", \"Portfolio.changed\")', 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_495",
    "$type": "Decision",
    "choice": "welcome-them",
    "reason": "ticked the Leaseline box on your other Startup’s sign-up at 10:04, address verified, and no Suppression covers Leaseline",
    "stakes": "low",
    "options": [
      "show-the-box",
      "ask-after-sign-up",
      "welcome-them",
      "hold-for-a-confirm",
      "send-the-launch",
      "to-the-ae",
  …
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 records one Consent naming Leaseline, stamped with the host, the Page, the words shown and the time; action_3 creates the Contact with the address, a first name and the region, and nothing else. action_4 is the welcome, sent after a 30-second hold.

+23 yeses per 100 sign-ups · ■■■□ Likely · n 260 · path → arr

Try another: the yesChoiceReason
Mia Chen ticked the Leaseline box on your other Startup’s sign-upwelcome-thema verified yes naming Leaseline; no Suppression
A sign-up whose address unsubscribed from Leaseline in June, now ticks the boxhold-for-a-confirma standing Suppression: one confirm request, nothing else
A welcome reply: “We have 200 units, what’s the price?”to-the-aeasks to buy, over 100 units
A sign-up already on Leaseline Pronot-nowalready buys from Leaseline: no box renders
Leaseline opens its owner-portal add-on on Nov 2send-the-launch61 family subscribers said yes to Leaseline; you approve the launch

Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Family opt-in rate → Sent by your other Startups → New Customers ↑ → ARR added each week → ARR

Worth’s formula: about +23 yeses per 100 sign-ups (your held-back read) × 120 sign-ups a month at your other Startup × 4 paid per 100 yeses × $347 first order ARR ≈ +$383. Under First signal: The yes rate reads within 14 days at 120 sign-ups a month; Customers from the yeses take about 90 days, so new-arr reads Too early to tell until January.

sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Contact.subscribed · first welcome 38s · 8 keys Not live yet

Start it this week

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

# 3 · keep it, at You approve the first wave
curl -X POST https://api.sb/leaseline.app/campaigns \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"play":"family-opt-in"}'

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

# off: the box comes off the host within 1 hour; every recorded yes stands
curl -X PATCH https://api.sb/leaseline.app/campaigns/family-opt-in \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endsAt":"now"}'
422Refusedfamily-opt-in can’t start yet: 1 check fails and 2 warn.
CheckFixThen
use-when pass · Not live yet, gaps #8None needed: 100 sign-ups a month is enough.POST /{namespace}/metrics/query
baseline warn · Not live yet, gaps #8The 1 in 10 shown no box is the comparison from day 1.POST /{namespace}/metrics/query
approved-pages fail · Not live yet, gaps #8Approve the box line and the welcome; each says only what your Page says.POST /{namespace}/approvals/{approval}/approve
role-unstaffed warn · Not live yet, gaps #8Staff the Marketing Lead, or leave it: buying replies come to you.POST /{namespace}/roles/{role}/staff
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "family-opt-in can’t start yet: 1 check fails and 2 warn.",
  "checks": [
    {
      "check": "use-when",
      "status": "pass",
      "observed": "2 Startups, buyer overlap 0.72; 141 sign-ups in 30 days at the host",
      "doc": "/plays/family-opt-in#check-use-when",
      "fix": "None needed: 100 sign-ups a month is enough.",
      "then": "Metric.query"
    },
    {
      "check": "baseline",
      "status": "warn",
      "observed": "0 yeses: no box has run",
      "doc": "/plays/family-opt-in#check-baseline",
      "fix": "The 1 in 10 shown no box is the comparison from day 1.",
      "then": "Metric.query"
    },
    {
      "check": "approved-pages",
      "status": "fail",
      "doc": "/plays/family-opt-in#check-approved-pages",
      "fix": "Approve the box line and the welcome; each says only what your Page says.",
      "then": "Approval.approve"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "marketing-lead",
        "sales-lead"
      ],
      "owned": 31,
      "doc": "/plays/family-opt-in#check-role-unstaffed",
      "fix": "Staff the Marketing Lead, or leave it: buying replies come to you.",
      "then": "Role.staff"
    }
  ]
}

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

CheckFix (one call)Your timeWait
use-when · baseline · pass: 2 Startups, overlap 0.72, 141 sign-ups a month; warn: no box has run (gaps #8)POST /{namespace}/metrics/query on family-opt-in-rate; the no-box tenth fills Before1 minnone
approved-pages · fail (Not live yet)POST /{namespace}/approvals/{approval}/approve the box line and the welcome10 minnone
role-unstaffed · warn (gaps #8): 31 of your open Leads have a person owner, youPOST /{namespace}/roles/{role}/staff, or leave it: buying replies come to you0 minnone

Your number · Before 0 · Now 23 yeses per 100 sign-ups · Target 20 by Dec 31 · ■■■□ Likely · held back at random · n 260

Each welcome and discovery note costs about $0.01 from Leaseline’s broadcast Mailbox (Worker cost); the box and the Consent cost nothing. That comes to about $1 a month at 30 yeses.

When it starts

WhenTrigger idStarts onWindowHow it gets in
Someone ticks the Leaseline box on your other Startup’s sign-upfamily-yesevent on Contact.subscribed where consent.recipient = receiverwelcome within 1 min; 1dthe host Form posts to POST /{namespace}/contacts/{contact}/subscribe
Someone unsubscribes or withdraws anywhere in your Startupsfamily-withdrawalevent on Consent.withdrawn where any Consent this Play recorded, at any scopebinds every Startup within 1 hPOST /{namespace}/consents/{consent}/withdraw, from any footer
+ 4 more for ask points, launches, a silent receiver and changes to your Startups (?lanes=all)
One signed delivery · StartupBuilder-Signature
{
  "$id": "https://api.sb/leaseline.app/events/event_1702",
  "$type": "Event",
  "name": "Contact.subscribed",
  "at": "2026-10-19T10:04:51Z",
  "seq": 1702,
  "data": {
    "contact": "contact_77",
    "host": "startup_2",
    "form": "form_3",
    "receiver": "leaseline.app",
    "words": "Leaseline: lease renewal software, same team as this app. Monthly updates.",
    "region": "us"
  }
}
Reproduce it in test
npx startup-builder trigger Contact.subscribed --like event_1702   # replays the delivery
npx startup-builder clock advance --by 3d   # then sends the discovery note, unless they replied
npx startup-builder listen Contact.subscribed --forward-to http://localhost:3000/hook

Who runs it

Marketing Lead · your agent (role:marketing-lead, worker_6) holds the Key Result for both Startups. Buying replies go to AE · Priya Shah (person) (role:sales-lead, worker_3), and each launch to your family subscribers comes to you (Dana Okafor, person, worker_1). Your number links to Did it work.

OptionRung todayEvidence (passed of n)Moves up atDrops back whenYou can
Show the box · Ask after sign-up · Not nowIt does it2,140 renders, 0 render checks failed—one render check fails → every box on that host comes offPATCH /{namespace}/forms/{id}
Welcome them · Hold for a confirmIt does it, you can stop it54 of 54 · 3 of 3met on day 6 (the first wave of 10)welcome unsubscribes over 8 % → the box comes offPOST /{namespace}/actions/{action}/cancel inside the hold
Send the launchYou approve each one0 in the windownever—POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse
To the AEIt does it2 of 2—a reply waits past 60 min → You approve each onePOST /{namespace}/actions/{action}/redirect
Change the box ruleYou approve each one0 mergednever—POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert
Never: a ticked or required box, a box on a payment step, anything they did at the host in a MessageTo a person, every time0 askednever—POST /{namespace}/tasks/{task}/claim

What it reads · What it may call. It reads the yes itself, the address, a first name and the region. It never passes the host’s plan, Form answers, Orders, Visits or usage to Leaseline, and no Message mentions anything they did at the host. Its tools are PATCH /{namespace}/forms/{id}, PATCH /{namespace}/pages/{id}, POST /{namespace}/consents, POST /{namespace}/messages/{message}/schedule, POST /{namespace}/approvals, POST /{namespace}/conversations/{conversation}/escalate, POST /{namespace}/tasks/{task}/assign, and any other call becomes an Approval under the Role’s Mandate.

The brief (folded): compiled Agent-register Markdown · play:family-opt-in@1 · each line names its record key
You hold the Marketing Lead Key Result: 20 yeses per 100 sign-ups by 2026-12-31. ← worker, metric
On each ask point, yes or withdrawal, choose one option and say why.             ← triggers, chooses
Render each box unticked, never required, naming Leaseline, same team and its cadence. ← copyRules 1, 2
Record one Consent per yes, naming Leaseline as recipient.                       ← your file, line 4
The welcome’s first line names the host and the date of the yes.                 ← copyRules 5
Show no box to 1 Visitor in 10, held for 90 days.                                ← your file, line 2

The slow loop. The weekly Decisions kept the box rule as written, and no Change was merged in the window, so there is no replay or read-back to print.

Bring your own agent (folded) starts at It does it, you can stop it on welcomes when it matches or beats the seated agent on the eval suite; renders stay rule-bound either way; POST /{namespace}/roles/{role}/staff seats it.

Terminal
npx startup-builder eval --scenario family-opt-in-260 --agent "claude -p" --seat marketing-lead   # seated: 23 yeses per 100, 2,140 of 2,140 renders passed, welcome in 38 s · yours: 23, 2,139 passed, 1 failed `render-check`

What it can do next

OptionPicked whenDecided byRunsHands toIf it’s just you
Show the box show-the-boxa host sign-up Form, buyer overlap 0.5 or moreYour rule (line 4)PATCH /{namespace}/forms/{id} with one unticked box, below every host fieldnobodythe same
Ask after sign-up ask-after-sign-upa smoke-test Page, or host completion fell 2 pointsYour rule (the completion Stop)PATCH /{namespace}/pages/{id}: two equal buttons after the act completesnobodythe same
Welcome them welcome-thema yes with a verified addressYour rule (line 4)POST /{namespace}/consents → family-welcome from Leaselinenobodythe same
Hold for a confirm hold-for-a-confirma standing Suppression, or a confirmed-yes regionAgent’s call, with its reasonPOST /{namespace}/messages/{message}/schedule one confirm request, nothing elsenobodythe same
Send the launch send-the-launchyou open an Offer, to Persons whose yes names youAgent’s call, with its reasonlaunch-sequence → POST /{namespace}/approvalsyou approve it, due 1 business day · Take this oneyou
To the AE to-the-aea welcome reply asks to buy, or a price for 100+ unitsAgent’s call, with its reasonPOST /{namespace}/conversations/{conversation}/escalate → POST /{namespace}/tasks/{task}/assignAE · Priya Shah, due 60 min · Take this oneyou, within 60 min
Not now not-nowalready subscribes or buys, declined twice in 180 days, or kept apartYour rule (the exclusion)nothing; no box renders for themnobodythe same
When they write backFixture replyWhat happensWho answersBy whenCall
Interested“We’d like to try it for our 200 units.”Priya offers times; the Brief goes with it (When it hands to you)AE · Priya Shah (person)60 minPOST /{namespace}/meetings/book
Send me pricing“What does Leaseline cost?”answers with the Pricing Page’s list Prices, $29 and $79 a monthMarketing Lead · your agent5 minPOST /{namespace}/conversations/{conversation}/answer
Not now“Just the updates for now.”the discovery note is skipped; the monthly update keeps comingnobodyat oncePOST /{namespace}/enrollments/{enrollment}/cancel
Wrong person“My business partner handles software.”asks them to forward the welcome; it never writes to an address from their textMarketing Lead · your agent1 business dayPOST /{namespace}/conversations/{conversation}/answer
Out of officean auto-reply until Oct 28the discovery note waits for the return datenobodythe return datePOST /{namespace}/messages/{message}/schedule
Question it can’t answer“Can one login cover both your apps?”To a person: no record answers it yet, so it says nothing of ityou1 business dayPOST /{namespace}/conversations/{conversation}/escalate
Stop“Unsubscribe.”withdrawn and suppressed for all your Startups within the hournobodyat oncePOST /{namespace}/consents/{consent}/withdraw

When it hands to you

Buying replies go to AE · Priya Shah (person); each launch to your family subscribers comes to you. One Brief per Role, as captured (brief is Not live yet, gaps #7).

What lands in each queue · sample
[
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_622",
    "$type": "Task",
    "assignee": "worker_3",
    "due": "2026-10-02T15:31:00Z",
    "on": "contact_71",
    "brief": {
      "who": "Sam Ortiz, 200 units",
      "said": "We’d like to try it for our 200 units.",
      "chose": "to-the-ae",
      "why": "asks to buy, over 100 units",
      "yes": "ticked the Leaseline box on your other Startup’s sign-up, 2026-09-28",
      "never": [
        "anything they did at the other Startup",
        "a discount"
      ]
    }
  },
  {
    "$id": "https://api.sb/leaseline.app/approvals/approval_74",
    "$type": "Approval",
    "verdict": null,
    "deadline": "2026-10-27T17:00:00Z",
    "draft": "Leaseline’s owner portal opens Nov 2: owners see every renewal in one place. You said yes to Leaseline updates on Sep 3 on our maintenance app.",
    "route": "send-the-launch",
    "checks": [
      {
        "check": "consent-stop",
        "status": "pass"
      },
      {
        "check": "claims-resolve",
        "status": "pass"
      }
    ]
  }
]

Priya and you answer with POST /{namespace}/tasks/{task}/claim (Take this one), POST /{namespace}/tasks/{task}/return with a reason, POST /{namespace}/approvals/{approval}/approve or POST /{namespace}/approvals/{approval}/refuse, each a one-call window; an Approval unanswered by its deadline expires and nothing sends. Once a person claims, the agent’s pending notes to that Person cancel with claimedByPerson (Not live yet, gaps #7). A Task nobody claims by its due time goes up through POST /{namespace}/tasks/{task}/escalate, and person-promise fires.

On time: AE · Priya Shah, 2 of 2 claimed within 60 min, median 26 min, n 2, ■□□□ Too early to tell beside Other Startups’ 95 % on time. Your queue: counted by kind: in week 1, 1 approval for the box line and welcome; since then about 1 hand-off a month and 1 launch approval.

The fixed line: POST /{namespace}/deals/{deal}/assign sets owner to the receiving person, the agent is never a Deal’s owner, and Meetings land on the owner’s calendar under the owner’s name.

When it won’t send

Check (refuses one send)RefusesFixThen
owned-by-a-person (rule j; Not live yet, gaps #8)a welcome or note to someone whose Lead or open Deal a person ownsLeave it to the person who owns it: the draft waits in their queue.POST /{namespace}/tasks/{task}/assign
consent-stop (gaps #3)a send to someone whose yes names your other Startup, not this oneAsk once for a yes to this Startup.POST /{namespace}/messages/{message}/schedule
render-check (the record’s Render check)a box that renders ticked, required, bundled, or without the name, same team or cadenceFix the box; every box on that host stays off until it passes.PATCH /{namespace}/forms/{id}
Stop (pauses the Play)Pauses whenThen
consent-audita Leaseline Contact from this Play with no Consent naming Leaselineevery Tactic pauses
host-completionhost completion in the boxed half 2 points under the no-box tenth, over 500 opensthat host moves to the after-sign-up ask only
welcome-unsubscribeunsubscribes over 8 % of welcomes, over 100the box comes off every host
complaintcomplaints near 0.1 % of family sendsfamily sends and boxes pause
welcome-laga yes with no welcome or confirm request inside 24 hthe box comes off until the backlog clears
silenceno family update outside the welcome for 45 daysthe box comes off until one sends
poola Startup leaves the pool, closes or turns cross-sell offits boxes come off within 1 h
suppression-auditany send to an address suppressed over 1 hevery family send pauses on every Startup
kill-switchyou press itevery Tactic pauses on every host; recorded yeses stand

The captured refusal, then the Stop that fired as a Campaign.paused reason (Not live yet, gaps #2); the box that rendered after the fix is the second thread in Example.

422RefusedThe welcome to contact_63 did not send: their yes names your maintenance-request Startup, not Leaseline.
CheckFixThen
consent-stop fail · Not live yet, gaps #3Ask once for a yes to this Startup.POST /{namespace}/messages/{message}/schedule
owned-by-a-person pass · Not live yet, gaps #8—POST /{namespace}/tasks/{task}/assign
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The welcome to contact_63 did not send: their yes names your maintenance-request Startup, not Leaseline.",
  "checks": [
    {
      "check": "consent-stop",
      "status": "fail",
      "doc": "/plays/family-opt-in#check-consent-stop",
      "fix": "Ask once for a yes to this Startup.",
      "then": "Message.schedule"
    },
    {
      "check": "owned-by-a-person",
      "status": "pass"
    }
  ]
}

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

The Stops that fired, as Campaign.paused reasons · sample
[
  {
    "reason": {
      "limit": "welcome-lag",
      "observed": "7 yeses with no welcome at 24 h",
      "bound": "0",
      "window": "24h"
    },
    "doc": "/plays/family-opt-in#stop-welcome-lag",
    "task": "task_617"
  }
]

Did it work

Each Visitor at the host was assigned at random, 9 in 10 to the box and 1 in 10 to no ask, so both halves read the same assignment (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).

+23 yeses per 100 sign-ups · ■■■□ Likely · n 260 · path → arr

Chose
welcome-them
Why
ticked the Leaseline box on your other Startup’s sign-up at 10:04, address verified, and no Suppression covers Leaseline
From
show-the-box · ask-after-sign-up · welcome-them · hold-for-a-confirm · send-the-launch · to-the-ae · not-now
Expects
the welcome delivered within 1 minute, and a Leaseline trial within 90d
Reopens when
Consent.withdrawn for contact_77, or no Message.delivered within 24h
By
worker_6
JSON
{
  "$id": "decisions/decision_495",
  "$type": "Decision",
  "choice": "welcome-them",
  "reason": "ticked the Leaseline box on your other Startup’s sign-up at 10:04, address verified, and no Suppression covers Leaseline",
  "stakes": "low",
  "options": [
    "show-the-box",
    "ask-after-sign-up",
    "welcome-them",
    "hold-for-a-confirm",
    "send-the-launch",
    "to-the-ae",
    "not-now"
  ],
  "by": "worker_6",
  "on": "contact_77",
  "expected": "the welcome delivered within 1 minute, and a Leaseline trial within 90d",
  "reopensWhen": "Consent.withdrawn for contact_77, or no Message.delivered within 24h"
}
NodeShown the box (234)No ask (26)AddedConfidenceBasis
family-opt-in-rate23 per 100 (54 of 234 sign-ups)0 (0 of 26)+23 [+18, +28]■■■□ Likelyheld back at random · n 260 · 31 inside their 14 days · minN 300
sibling-sourced-sharenew Leaseline Customers from boxed sign-ups: 0.9 per 100 (2 of 234)0 (0 of 26)+0.9 [−0.4, +2.1] · Not carrying up yet■□□□ Too early to tellthe same assignment · Other Startups’ results +1.4 per 100, ■■□□ Early sign, n 18,300 sign-ups
new-arr ↑$694, 2 Customers on Pro, kind family-opt-in from your other Startup · Booked: credited, not all caused by this Play$0+$694 [−$350, +$1,800] · Worth about +$380 a month■□□□ Too early to tell (2 vs 0 Customers)the same · 6 trials still deciding
net-new-arr · arr+$694$0+$694, carried: it moves no other bridge term■□□□ Too early to tellcarried from new-arr

The leaf counts yeses per 100 completed sign-ups at the host’s ask point, over 14 days, to 300; at 260 the Experiment runs on. The parent reads Leaseline Customers per 100 host sign-ups in each arm, 90 days out.

How it moves it: one honest, unticked box on the host’s sign-up, and a welcome from Leaseline 38 seconds after the yes, naming the host and the date.

Guardrails: boxed against no ask: host sign-up completion, 71 % against 73 %, inside the 2-point Stop, and welcome unsubscribes, 3.7 % of 54 against the 8 % floor; 6 trials are still deciding.

The Campaign card · sample
{
  "$id": "https://api.sb/leaseline.app/campaigns/family-opt-in",
  "$type": "Campaign",
  "play": "play:family-opt-in@1",
  "path": [
    "family-opt-in-rate",
    "sibling-sourced-share",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "family-opt-in-rate",
    "unit": "per-100",
    "heldBack": 26,
    "decision": null
  },
  "readout": {
    "value": 23,
    "n": 260,
    "maturing": 31,
    "interval": [
      18,
      28
    ],
    "readability": "B",
    "basis": "held-back",
    "pooled": false
  },
  "ledger": {
    "spend": 0,
    "workerCost": 1,
    "hours": 1.1,
    "openArr": 2082,
    "wonArr": 694,
    "projectedArr": 383,
    "paybackDays": 1
  }
}

Outside benchmark: no measured range for a same-team box survived the research’s check, so Other Startups’ results (n 18,300 host sign-ups) stand in.

Before it · After it. Before it: your other Startup’s own Plays bring the sign-ups that see the box. After it: owned-newsletter hears every yes as a new subscriber, and instant-response answers the ones who write in.

Example

Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days) on your other Startup’s sign-up: 2,140 Form opens, 260 completed sign-ups, 234 shown the box and 26 shown no ask. Read every captured thread at ?threads=all; three follow the tables.

What happened, by optionDecisionsDoneAfter
Show the box · Not now2,140 renders · 1180 render checks failed · 118 already Leaseline Customers or subscribers—
Welcome them · Hold for a confirm54 · 354 welcomes, median 38 s · 3 confirm requests, 1 pressed2 unsubscribed; 8 trials
To the AE · Send the launch2 · 02 hand-offs · the Nov 2 launch awaits you1 Meeting, 1 Deal open
Shown the box · no ask234 · 2654 yeses · 02 Customers ($694) · 0
DayEventWhat happenedYour time
0event_1660 Campaign.createdthe three steps; the dry run fails approved-pages20 min
0event_1662 Approval.approvedyou approved the box line and the welcome10 min
1event_1665 Contact.subscribedthe first yes; the welcome landed in 41 s—
6event_1671 Role.updatedpromoted: welcomes send after a 30-second hold—
23event_1684 Campaign.pausedwelcome-lag: a Mailbox token expired and 7 yeses waited; reconnected in 2 h, all 7 welcomed15 min
39event_1693 Offer.subscribedthe first Leaseline Customer from a family yes, on Pro—
1–56event_1663 POST /{namespace}/tasks/next, weekly2 hand-offs and the weekly review, from the POST /{namespace}/tasks/next log25 min · total 1.1 h measured, 1 h asked

Leaseline → Mia Chen, Sep 3, 09:12:40 · You said yes to Leaseline updates on our maintenance-request app at 09:12 today.¹ Leaseline sends every renewal notice 60 days out, per owner; updates come monthly.² Signed with the Role’s disclosure line. Footer: where and when you said yes, and one-click unsubscribe.³

Mia, Sep 6 · replied to the discovery note: “Renewals are our biggest headache.” She started a trial on Sep 9 and moved to Pro on Oct 4.

The box line, 2026-08-24 · the draft render-check refused, then the box that rendered
- ☑ Get Leaseline updates (recommended)
+ ☐ Leaseline: lease renewal software, same team as this app. Monthly updates.

Sam Ortiz, 2026-10-02 14:31 · “We’d like to try it for our 200 units.” · Leaseline, 14:33 · *Thanks, Sam. Priya, our account lead, replies with times by 15:31.*⁴ The Brief went to Priya (task_622); she wrote at 14:57.

¹ The first line names the host and the date of the yes. ² What Leaseline makes, and its cadence. ³ Every footer says where and when they said yes. ⁴ The routed line, compiled from the Role’s Settings.

Your settings

plays/family-opt-in.mdx
---
holdout: 0.1    # 1 Visitor in 10 at the host sees no box, sticky 90 days
---
One unticked Leaseline box on the maintenance app’s sign-up, below every field. Welcome each yes from Leaseline; buying replies go to Priya.
Text
$ npx startup-builder show plays/family-opt-in
holdout 0.1 · 1 box · welcome from Leaseline · AE on buying replies     your file  plays/family-opt-in.mdx:2,4 (Ask 2026-08-24)
worker role:marketing-lead, owns never · chooses 7 (4 rule, 3 read) · sequences family-welcome, launch-sequence, launch-note, closing-note · approval first-send · checks owned-by-a-person, consent-stop, render-check · stops 9 · placements portfolio-network, gmail, outlook, yahoo-mail    catalog  play:family-opt-in@1 (Not live yet, gaps #5)

Every key from the catalog

KeyIdWhat it is
Tacticfamily-pairingFamily pairing picks up to six of your other Startups a week whose ICP overlaps a host’s and whose box line is approved, ranked by expected yes rate. Starts at You approve the first wave.
Tacticfamily-askFamily ask renders at most two unticked boxes on a host Form, each naming one of your other Startups, or one equal-button ask after the act completes. Starts at Sends nothing.
Tacticfamily-consentFamily consent records one Consent per yes naming its recipient, and hands the receiver only the address, a first name, the region and the Consent. Starts at Sends nothing.
Tacticfamily-welcomeFamily welcome sends a new family subscriber the receiver’s welcome inside a minute, naming the host and date of the yes, then one discovery note. Starts at You approve the first wave.
Tacticlaunch-dayLaunch day opens a receiver’s Offer to every family subscriber at once, beside its direct Waitlist, and asks hosts’ launch-list subscribers once each. Starts at You approve each one.
Tacticfamily-withdrawalFamily withdrawal makes one opt-out at any of your Startups stop all of them within the hour, and ends or narrows family Consents only as the Person chooses. Starts at Sends nothing.
Tacticfamily-readoutFamily readout reads opt-ins per 100 sign-ups, the host completion cost and receiver Customers against the no-ask arm and the stepped wedge. Starts at Sends nothing.
Tacticsibling-exclusionSibling 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.
Triggerask-point-openAsk point open: event on Form.opened, window 1h.
Triggerfamily-yesFamily yes: event on Contact.subscribed, window 1d.
Triggerfamily-withdrawalFamily withdrawal: event on Consent.withdrawn, window 1h.
Triggerreceiver-launchReceiver launch: event on Offer.published, window 3d.
Triggerreceiver-silentReceiver silent: state on Brand.lapsed, window 15d.
Triggerpool-changePool change: event on Portfolio.changed, window 7d.
Triggersibling-offer-changeSibling offer change: event on Offer.published, window 7d.
Sequencefamily-welcomeFamily welcome: 2 steps; it ends on The Person unsubscribes.; The Person withdraws the Consent.; The Person is suppressed.; The discovery note is sent or skipped, and the receiver’s own cadence takes over..
Sequencelaunch-sequenceLaunch sequence: 4 steps; it ends on The Person signs up.; The Person pays.; The Person replies.; The Person unsubscribes, withdraws or is suppressed..
Sequencelaunch-noteLaunch note: 1 steps; it ends on The note is sent.; The Person is suppressed or unsubscribes before the send..
Sequenceclosing-noteClosing note: 1 steps; it ends on The note is sent..

Compiled, not written: edit the Play record family-opt-in 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.