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.
- Step
- Earn referrals
- Channel
- Newsletter
- Kind
- Owned audience
Be our Marketing Lead across my two Startups: put one unticked Leaseline box on my maintenance-request app’s sign-up, welcome every yes from Leaseline within a minute, and leave 1 in 10 with no box so we can tell. Show me what’s missing; don’t send.
# Not live yet (gaps #2)
curl -X POST "https://api.sb/leaseline.app/campaigns?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"play":"family-opt-in"}'import { $ } from 'startup-builder'
return await $.Campaign.create({ play: 'family-opt-in' }, { mode: 'try' }) // dry run on the day-0 Startup, 2026-08-24 · Not live yet, gaps #2
npx startup-builder do Campaign.create --json '{"play":"family-opt-in"}' --try # Not live yet (gaps #2){
"name": "do",
"arguments": {
"code": "return await $.Campaign.create({ play: 'family-opt-in' }, { mode: 'try' }) // dry run on the day-0 Startup, 2026-08-24 · Not live yet, gaps #2",
"mode": "try"
}
}application/json{
"type": "https: //startupbuilder.dev/ errors/ refused",
"title": "Refused",
"status": 422,
"detail": "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"
},
…
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"
}
}application/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 yetPreviewOn 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 yes | Choice | Reason |
|---|---|---|
| Mia Chen ticked the Leaseline box on your other Startup’s sign-up | welcome-them | a verified yes naming Leaseline; no Suppression |
| A sign-up whose address unsubscribed from Leaseline in June, now ticks the box | hold-for-a-confirm | a standing Suppression: one confirm request, nothing else |
| A welcome reply: “We have 200 units, what’s the price?” | to-the-ae | asks to buy, over 100 units |
| A sign-up already on Leaseline Pro | not-now | already buys from Leaseline: no box renders |
| Leaseline opens its owner-portal add-on on Nov 2 | send-the-launch | 61 family subscribers said yes to Leaseline; you approve the launch |
- 40 min week 1, then 5 a week
- Your time
- Day 1: first yes and its welcome
- First signal
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/
Start it this week
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"}' | Check | Fix | Then |
|---|---|---|
use-when pass · Not live yet, gaps #8 | None needed: 100 sign-ups a month is enough. | POST /{namespace}/metrics/query |
baseline warn · Not live yet, gaps #8 | The 1 in 10 shown no box is the comparison from day 1. | POST /{namespace}/metrics/query |
approved-pages fail · Not live yet, gaps #8 | Approve 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 #8 | Staff 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.
| Check | Fix (one call) | Your time | Wait |
|---|---|---|---|
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 Before | 1 min | none |
approved-pages · fail (Not live yet) | POST /{namespace}/approvals/{approval}/approve the box line and the welcome | 10 min | none |
role-unstaffed · warn (gaps #8): 31 of your open Leads have a person owner, you | POST /{namespace}/roles/{role}/staff, or leave it: buying replies come to you | 0 min | none |
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
| When | Trigger id | Starts on | Window | How it gets in |
|---|---|---|---|---|
| Someone ticks the Leaseline box on your other Startup’s sign-up | family-yes | event on Contact.subscribed where consent.recipient = receiver | welcome within 1 min; 1d | the host Form posts to POST /{namespace}/contacts/{contact}/subscribe |
| Someone unsubscribes or withdraws anywhere in your Startups | family-withdrawal | event on Consent.withdrawn where any Consent this Play recorded, at any scope | binds every Startup within 1 h | POST /{namespace}/consents/{consent}/withdraw, from any footer |
+ 4 more for ask points, launches, a silent receiver and changes to your Startups (?lanes=) |
{
"$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"
}
}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:, worker_6) holds the Key Result for both Startups. Buying replies go to AE · Priya Shah (person) (role:, worker_3), and each launch to your family subscribers comes to you (Dana Okafor, person, worker_1). Your number links to Did it work.
| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
|---|---|---|---|---|---|
| Show the box · Ask after sign-up · Not now | It does it | 2,140 renders, 0 render checks failed | — | one render check fails → every box on that host comes off | PATCH /{namespace}/forms/{id} |
| Welcome them · Hold for a confirm | It does it, you can stop it | 54 of 54 · 3 of 3 | met on day 6 (the first wave of 10) | welcome unsubscribes over 8 % → the box comes off | POST /{namespace}/actions/{action}/cancel inside the hold |
| Send the launch | You approve each one | 0 in the window | never | — | POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse |
| To the AE | It does it | 2 of 2 | — | a reply waits past 60 min → You approve each one | POST /{namespace}/actions/{action}/redirect |
| Change the box rule | You approve each one | 0 merged | never | — | 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 Message | To a person, every time | 0 asked | never | — | 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.
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.
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
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
|---|---|---|---|---|---|
Show the box show-the-box | a host sign-up Form, buyer overlap 0.5 or more | Your rule (line 4) | PATCH /{namespace}/forms/{id} with one unticked box, below every host field | nobody | the same |
Ask after sign-up ask-after-sign-up | a smoke-test Page, or host completion fell 2 points | Your rule (the completion Stop) | PATCH /{namespace}/pages/{id}: two equal buttons after the act completes | nobody | the same |
Welcome them welcome-them | a yes with a verified address | Your rule (line 4) | POST /{namespace}/consents → family-welcome from Leaseline | nobody | the same |
Hold for a confirm hold-for-a-confirm | a standing Suppression, or a confirmed-yes region | Agent’s call, with its reason | POST /{namespace}/messages/{message}/schedule one confirm request, nothing else | nobody | the same |
Send the launch send-the-launch | you open an Offer, to Persons whose yes names you | Agent’s call, with its reason | launch-sequence → POST /{namespace}/approvals | you approve it, due 1 business day · Take this one | you |
To the AE to-the-ae | a welcome reply asks to buy, or a price for 100+ units | Agent’s call, with its reason | POST /{namespace}/conversations/{conversation}/escalate → POST /{namespace}/tasks/{task}/assign | AE · Priya Shah, due 60 min · Take this one | you, within 60 min |
Not now not-now | already subscribes or buys, declined twice in 180 days, or kept apart | Your rule (the exclusion) | nothing; no box renders for them | nobody | the same |
| When they write back | Fixture reply | What happens | Who answers | By when | Call |
|---|---|---|---|---|---|
| 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 min | POST /{namespace}/meetings/book |
| Send me pricing | “What does Leaseline cost?” | answers with the Pricing Page’s list Prices, $29 and $79 a month | Marketing Lead · your agent | 5 min | POST /{namespace}/conversations/{conversation}/answer |
| Not now | “Just the updates for now.” | the discovery note is skipped; the monthly update keeps coming | nobody | at once | POST /{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 text | Marketing Lead · your agent | 1 business day | POST /{namespace}/conversations/{conversation}/answer |
| Out of office | an auto-reply until Oct 28 | the discovery note waits for the return date | nobody | the return date | POST /{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 it | you | 1 business day | POST /{namespace}/conversations/{conversation}/escalate |
| Stop | “Unsubscribe.” | withdrawn and suppressed for all your Startups within the hour | nobody | at once | POST /{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).
[
{
"$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) | Refuses | Fix | Then |
|---|---|---|---|
owned-by-a-person (rule j; Not live yet, gaps #8) | a welcome or note to someone whose Lead or open Deal a person owns | Leave 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 one | Ask 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 cadence | Fix the box; every box on that host stays off until it passes. | PATCH /{namespace}/forms/{id} |
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.
| Check | Fix | Then |
|---|---|---|
consent-stop fail · Not live yet, gaps #3 | Ask 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.
[
{
"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"
}| Node | Shown the box (234) | No ask (26) | Added | Confidence | Basis |
|---|---|---|---|---|---|
family-opt-in-rate | 23 per 100 (54 of 234 sign-ups) | 0 (0 of 26) | +23 [+18, +28] | ■■■□ Likely | held back at random · n 260 · 31 inside their 14 days · minN 300 |
sibling-sourced-share | new 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 tell | the 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 tell | carried 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.
{
"$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=; three follow the tables.
| What happened, by option | Decisions | Done | After |
|---|---|---|---|
| Show the box · Not now | 2,140 renders · 118 | 0 render checks failed · 118 already Leaseline Customers or subscribers | — |
| Welcome them · Hold for a confirm | 54 · 3 | 54 welcomes, median 38 s · 3 confirm requests, 1 pressed | 2 unsubscribed; 8 trials |
| To the AE · Send the launch | 2 · 0 | 2 hand-offs · the Nov 2 launch awaits you | 1 Meeting, 1 Deal open |
| Shown the box · no ask | 234 · 26 | 54 yeses · 0 | 2 Customers ($694) · 0 |
| Day | Event | What happened | Your time |
|---|---|---|---|
| 0 | event_1660 Campaign.created | the three steps; the dry run fails approved-pages | 20 min |
| 0 | event_1662 Approval.approved | you approved the box line and the welcome | 10 min |
| 1 | event_1665 Contact.subscribed | the first yes; the welcome landed in 41 s | — |
| 6 | event_1671 Role.updated | promoted: welcomes send after a 30-second hold | — |
| 23 | event_1684 Campaign.paused | welcome-lag: a Mailbox token expired and 7 yeses waited; reconnected in 2 h, all 7 welcomed | 15 min |
| 39 | event_1693 Offer.subscribed | the first Leaseline Customer from a family yes, on Pro | — |
| 1–56 | event_1663 POST /{namespace}/tasks/next, weekly | 2 hand-offs and the weekly review, from the POST /{namespace}/tasks/next log | 25 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.
- ☑ 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
---
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. $ 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
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.