Next order due
Tells a Customer the day their last order makes the next one due, with the live price and a one-click order.
- Step
- Keep Customers
- Channel
- More seats and upgrades
- Kind
- Owned audience
- Summary
- startupbuilder.dev
Be our CSM: when a Renewal audit we delivered comes due again, tell the Customer 30 days ahead with the price and a one-click order. Mail a printed notice too. If someone asks to change the scope, bring in our AE. 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":"next-outcome"}'import { $ } from 'startup-builder'
return await $.Campaign.create({ play: 'next-outcome' }, { 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":"next-outcome"}' --try # Not live yet (gaps #2){
"name": "do",
"arguments": {
"code": "return await $.Campaign.create({ play: 'next-outcome' }, { 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": "next-outcome can’t start yet: 1 check fails and 2 warn.",
"checks": [
{
"check": "use-when",
"status": "pass",
"observed": "22 Customers with a delivered Renewal audit, 7 due a month",
"doc": "/plays/ next-outcome#check-use-when",
"fix": "Nothing to do: it counts Customers whose next outcome comes due.",
"then": "Metric.query"
},
…
Hand our latest Customer whose next audit came due to the CSM agent and show me what it chose and why. Don’t send.
curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Order.delivered%22%2C%20%22Schedule.fired%22%2C%20%22Order.placed%22%2C%20%22Order.refunded%22%2C%20%22Offer.published%22%2C%20%22Message.received%22%2C%20%22Renewal.opened%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_1388"}'
# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/decisions?where=on%20%3D%20%22customer_23%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%22customer_23%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"import { $ } from 'startup-builder'
const { events: [last] } = await $.Event.list({ where: 'name in ("Order.delivered", "Schedule.fired", "Order.placed", "Order.refunded", "Offer.published", "Message.received", "Renewal.opened")', 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 (\"Order.delivered\", \"Schedule.fired\", \"Order.placed\", \"Order.refunded\", \"Offer.published\", \"Message.received\", \"Renewal.opened\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_1388"}' --try # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"customer_23\""}' # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"customer_23\""}' # Not live yet (gaps #7){
"name": "do",
"arguments": {
"code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Order.delivered\", \"Schedule.fired\", \"Order.placed\", \"Order.refunded\", \"Offer.published\", \"Message.received\", \"Renewal.opened\")', 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_455",
"$type": "Decision",
"choice": "send-due-notice",
"reason": "Cedar Point Rentals’ Renewal audit was delivered 2025-11-18, so the next is due 2026-11-18; the map row is approved, and $450 is over the $100 print line",
"stakes": "low",
"options": [
"send-due-notice",
"offer-every-year",
"late-note",
"to-the-owner",
"not-now"
],
…
sample · 4 Not live yetPreviewOn PATCH /{namespace}/campaigns/{id}Off PATCH /{namespace}/campaigns/{id}Take this one POST /{namespace}/tasks/{task}/claim
action_3 is the email notice to contact_14, Cedar Point’s billing owner, held 2 minutes. action_4 is the printed notice on day 16; an Order before then cancels it.
50 per 100 against 19 before · before-after,
| Try another: the Customer | Choice | Reason |
|---|---|---|
| Cedar Point Rentals: Renewal audit delivered 2025-11-18, next due 2026-11-18 | send-due-notice | due within the 30-day lead time; the row is approved |
| Juniper Homes: ordered the audit on time 2 years running | offer-every-year | two on-time Orders: every-year terms at the same Price, for your OK |
| Maple Court: audit due Oct 9, no Order by Oct 12 | late-note | the row allows a late audit; one note at the late Price |
| Fairmont Lofts: invoice past due since Oct 1 | not-now | keep-first: their CSM sees them first |
- 30 min week 1, then 5 a week
- Your time
- Day 3: first next audit ordered
- First signal
Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Next outcome acceptance → Won back ↑ → ARR added each week → ARR
Worth’s formula: about +22 Orders per 100 due (Other Startups’ results; no lift of your own yet) × 7 due a month ≈ 1.5 more Orders a month; about 1.4 sit in any 28 days × $450 × 13 ≈ +$8,200 ARR, held while it runs. Under First signal: At 7 due a month, 90 days can tell only +45 per 100 or more, so your file starts at holdout: and reads before-after.
sample · api.sb/
Start it this week
npx startup-builder add play next-outcome # 1 · writes plays/next-outcome.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":"next-outcome"}'
# 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":"next-outcome"}'
# on
curl -X PATCH https://api.sb/leaseline.app/campaigns/next-outcome \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"endsAt":null}'
# off: no notice sends; every-year charge notices and their cancel links keep sending
curl -X PATCH https://api.sb/leaseline.app/campaigns/next-outcome \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"endsAt":"now"}' | Check | Fix | Then |
|---|---|---|
use-when pass · Not live yet, gaps #8 | Nothing to do: it counts Customers whose next outcome comes due. | POST /{namespace}/metrics/query |
billing-connected fail · Not live yet, gaps #8 | Connect Stripe so every Order, refund and live Price is real. | POST /{namespace}/integrations/connect |
baseline warn · Not live yet, gaps #8 | n 16: it reads before-after, not proof, until volume allows a held-back half. | POST /{namespace}/metrics/query |
owned-by-a-person pass · Not live yet, gaps #8 | Nothing to do: their notices draft into your queue instead. | POST /{namespace}/tasks/{task}/assign |
keep-first pass · Not live yet, gaps #8 | Nothing to do: a past-due Customer gets no notice until their CSM clears them. | POST /{namespace}/tasks/next |
customer-ask-cap pass · Not live yet, gaps #3 | Nothing to do: one open ask per Customer, across every Play. | GET /{namespace}/decisions |
role-unstaffed warn · Not live yet, gaps #8 | Staff the CSM and AE, or leave them: those hand-offs come to you. | POST /{namespace}/roles/{role}/staff |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "next-outcome can’t start yet: 1 check fails and 2 warn.",
"checks": [
{
"check": "use-when",
"status": "pass",
"observed": "22 Customers with a delivered Renewal audit, 7 due a month",
"doc": "/plays/next-outcome#check-use-when",
"fix": "Nothing to do: it counts Customers whose next outcome comes due.",
"then": "Metric.query"
},
{
"check": "billing-connected",
"status": "fail",
"doc": "/plays/next-outcome#check-billing-connected",
"fix": "Connect Stripe so every Order, refund and live Price is real.",
"then": "Integration.connect"
},
{
"check": "baseline",
"status": "warn",
"observed": "3 of 16 ordered with no notice in 90 days",
"doc": "/plays/next-outcome#check-baseline",
"fix": "n 16: it reads before-after, not proof, until volume allows a held-back half.",
"then": "Metric.query"
},
{
"check": "owned-by-a-person",
"status": "pass",
"observed": "2 of the 22 have a person owner, you",
"doc": "/plays/next-outcome#check-owned-by-a-person",
"fix": "Nothing to do: their notices draft into your queue instead.",
"then": "Task.assign"
},
{
"check": "keep-first",
"status": "pass",
"observed": "1 past due, held",
"doc": "/plays/next-outcome#check-keep-first",
"fix": "Nothing to do: a past-due Customer gets no notice until their CSM clears them.",
"then": "Task.next"
},
{
"check": "customer-ask-cap",
"status": "pass",
"observed": "0 open asks",
"doc": "/plays/next-outcome#check-customer-ask-cap",
"fix": "Nothing to do: one open ask per Customer, across every Play.",
"then": "Decision.list"
},
{
"check": "role-unstaffed",
"status": "warn",
"roles": [
"success-manager",
"sales-lead"
],
"owned": 2,
"doc": "/plays/next-outcome#check-role-unstaffed",
"fix": "Staff the CSM and AE, or leave them: those hand-offs come to you.",
"then": "Role.staff"
}
]
}It says no, says why, and names the fix.
| Check | Fix (one call) | Your time | Wait |
|---|---|---|---|
use-when · pass: 22 Customers, 7 due a month (gaps #8) | POST /{namespace}/metrics/query on next-outcome-acceptance | 1 min | none |
billing-connected · fail | POST /{namespace}/integrations/connect with system recurring-billing (Stripe) | 5 min | the first Order sync, about 10 min |
baseline · warn: 3 of 16 in 90 days | POST /{namespace}/metrics/query; it fills Before in Your number | 0 min | none |
owned-by-a-person · keep-first · customer-ask-cap · pass: 2 owned by you; 1 past due held; 0 open asks (gaps #3, #8) | nothing; each runs again on every notice | 0 min | none |
role-unstaffed · warn: 2 of your 22 have a person owner, you | POST /{namespace}/roles/{role}/staff, or leave it: CSM and AE hand-offs come to you, and your 2 get drafts in your queue | 0 min | none |
Your number · Before 19 per 100 · Now 50 per 100 · Target 45 by Dec 31 · before-after,
Each email notice costs $0.04 from your Domain (Worker cost), and each printed notice about $1.10, under a $100-a-month print Limit. The outcome map and the one-click Order Page cost nothing. That comes to about $7 a month at 7 due with print, and $1 without.
When it starts
| When | Trigger id | Starts on | Window | How it gets in |
|---|---|---|---|---|
| A next audit comes within 30 days of its due date | next-outcome-due | at on Schedule.fired where dueAt - 30d | 30d, to the due date | the outcome map schedules it from the delivered Order; POST /{namespace}/orders/{order}/deliver writes the date |
| You deliver a Renewal audit | service-order-delivered | event on Order.delivered where offer.kind = 'service' | 1d | POST /{namespace}/orders/{order}/deliver, or POST /{namespace}/integrations/connect (Stripe) |
| A due date passed 3 days ago with no Order, and the row allows a late audit | next-outcome-overdue | state on Schedule.fired where status = 'overdue' and lateAllowed = true | 14d | a state on the outcome; no call |
+ 5 more: an Order placed or refunded, a changed map row, a done-elsewhere reply, and a yearly charge coming due (?lanes=) |
{
"$id": "https://api.sb/leaseline.app/events/event_1388" ,
"$type": "Event",
"name": "Schedule.fired",
"at": "2026-10-19T07:00:04Z" ,
"seq": 1388,
"data": {
"customer": "customer_23",
"outcome": "renewal-audit",
"from": "orders/order_61" ,
"delivered": "2025-11-18",
"dueAt": "2026-11-18",
"price": 450,
"currency": "usd"
}
}npx startup-builder trigger Schedule.fired --like event_1388 # replays the delivery in test
npx startup-builder clock advance --by 16d # runs the printed notice (action_4) unless an Order lands first
npx startup-builder listen Order.placed --forward-to http://localhost:3000/hook Who runs it
CSM · your agent (role:, worker_5) holds the Key Result. It hands to each Customer’s owner: AE · Priya Shah (person) (worker_3, staffed on day 9) when they ask to change the scope or the price, and you (Dana Okafor, person, worker_1) for every-year terms and for the Customers you own, because the CSM Role has no person. Your number links to Did it work.
| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
|---|---|---|---|---|---|
| Send the due notice | It does it, you can stop it | 9 of 9 approved unedited | met on day 15 (the first 5 notices, then 4 more) | a sent date or Price differs (wrong-date, misquote) → You approve each one | POST /{namespace}/actions/{action}/cancel inside the 2-minute hold |
| Send the late note · Not now | It does it, you can stop it | 2 of 2 · 4 of 4 | the same | the same | the same |
| Offer every-year terms | You approve each one | 2 of 2 | never: a yearly charge is yours to offer | — | POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse |
| To the owner | It does it | 1 of 1 | — | a held Customer is asked anyway → You approve each one | POST /{namespace}/actions/{action}/redirect |
| Change the outcome map | You approve each one | 1 merged of 1 | never | — | POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert |
| Never: a date the map didn’t compute, a consequence past the approved line, a discount | Refused, every time | 1 drafted, 1 refused | never | — | none: the Check holds it |
What it reads · What it may call. It reads the delivered Order, the outcome map’s row and the live Price, and never a Visit or a click. POST /{namespace}/orders/place is the Customer’s own press on the one-click Page, never the agent’s call. Its tools are 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 CSM Key Result: 45 next audits ordered per 100 due by 2026-12-31. ← worker, metric
On each outcome that comes due, choose one option and say why; a held Customer gets Not now. ← triggers, chooses, keep-first
The due date is the one the outcome map computes from their own delivered Order. ← copyRules 1
Every Price is the live Price; no consequence past the approved line. ← copyRules 2–3
Say it is an offer from Leaseline; never say the audit has started. ← copyRules 5–6
Every notice carries a done-elsewhere link and an unsubscribe link. ← copyRules 7The slow loop, as a Change. decision_377 (2026-09-21, weekly) read 4 notices sent 30 days out with 1 Order by the due date, and 3 Orders in the week after it. change_29 added a reminder on the due date itself (your file, line 5), and Dana merged it with POST /{namespace}/changes/{change}/merge. Replayed on past notices, POST /{namespace}/changes/{change}/review put the row at 58 per 100; it has no read-back yet: 2 notices have reached their window since.
Bring your own agent (folded) starts at It does it, you can stop it when it matches or beats the seated agent on the eval suite, and at You approve each one when it does not; POST /{namespace}/roles/{role}/staff seats it.
npx startup-builder eval --scenario next-outcome-31 --agent "claude -p" --seat success-manager # seated: 0 wrong dates, 0 Checks failed sent, $0.04 a notice · yours: 1 wrong date → You approve each one What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
|---|---|---|---|---|---|
Send the due notice send-due-notice | due within 30 days, row approved | Your rule (line 4) | next-outcome-due-notice: email on day 0, print on day 16 at $100 or more, reminders → POST /{namespace}/messages/{message}/schedule | the Customer’s press on the one-click Order | the same |
Offer every-year terms offer-every-year | they ordered the same audit on time before | Agent’s call, with its reason | next-outcome-due-notice with every-year terms at the same Price → POST /{namespace}/approvals; on yes, next-outcome-yearly-notice before each charge | you approve the terms · Take this one | you |
Send the late note late-note | due 3 days ago, no Order, the row allows late | Agent’s call, with its reason | next-outcome-overdue-note, one note at the late Price | nobody | the same |
To the owner to-the-owner | a person owns them, or they ask to change the scope or the price | Agent’s call, with its reason | POST /{namespace}/conversations/{conversation}/escalate → POST /{namespace}/tasks/{task}/assign | AE · Priya Shah, due 1 business day · Take this one | you |
Not now not-now | already ordered or done elsewhere, another Play holds them, or keep-first applies | Your rule (rule j) | nothing; names the holder | their CSM, so you · Take this one | you |
| When they write back | Fixture reply | What happens | Who answers | By when | Call |
|---|---|---|---|---|---|
| Interested | “Yes, book the November audit.” | the notice’s one-click Order Page; nothing is charged until they press | the Customer | — | POST /{namespace}/orders/place (theirs) |
| Send me pricing | “Is it still $450?” | the live Price and what the audit covers, from the Offer | CSM · your agent | 5 min | POST /{namespace}/conversations/{conversation}/answer |
| Not now | “We did it in-house this year.” | the done-elsewhere answer: the outcome closes, and customer-ask-cap frees the Customer | nobody | at once | GET /{namespace}/decisions |
| Wrong person | “Our controller, Lee, books these.” | asks them to forward it; it never writes to an address from their text | CSM · your agent | 1 business day | POST /{namespace}/conversations/{conversation}/answer |
| Out of office | an auto-reply until Oct 28 | the reminder waits for the return date, inside the lead time | nobody | the return date | POST /{namespace}/messages/{message}/schedule |
| Question it can’t answer | “Can you add our 2 new owners to the same audit?” | To the owner: scope and price are never the agent’s call | AE · Priya Shah (person) | 1 business day | POST /{namespace}/conversations/{conversation}/escalate |
| Stop | “Stop sending these.” | notices stop on every lane; a yearly charge notice and its cancel link still send | nobody | at once | POST /{namespace}/contacts/{contact}/unsubscribe |
When it hands to you
On this Play, hand-offs go to the Customer’s owner, the CSM’s seat: you for every-year terms and the Customers you own, and AE · Priya Shah (person) for a change of scope or price. One Brief per Role, as captured (brief is Not live yet, gaps #7).
[
{
"$id": "https://api.sb/leaseline.app/tasks/task_471" ,
"$type": "Task",
"assignee": "worker_3",
"due": "2026-10-09T16:20:00Z" ,
"on": "customer_7",
"brief": {
"who": "Birchway Properties",
"said": "Can you add our 2 new owners to the same audit?" ,
"chose": "to-the-owner",
"why": "a change of scope is a Quote, the AE’s" ,
"due": "next audit 2026-11-02",
"never": [
"a discount",
"a date the map didn’t compute"
]
}
},
{
"$id": "https://api.sb/leaseline.app/approvals/approval_64" ,
"$type": "Approval",
"verdict": "approved",
"deadline": "2026-09-30T17:00:00Z" ,
"draft": "Your next Renewal audit is due Oct 29. You’ve booked it on time twice; you can have it booked every year at $450…" ,
"route": "offer-every-year",
"checks": [
{
"check": "fact-check",
"status": "pass"
},
{
"check": "keep-first",
"status": "pass"
}
]
},
{
"$id": "https://api.sb/leaseline.app/tasks/task_458" ,
"$type": "Task",
"assignee": "worker_1",
"due": "2026-10-02T17:00:00Z" ,
"on": "customer_31",
"brief": {
"who": "Fairmont Lofts",
"chose": "not-now",
"why": "invoice past due since Oct 1",
"held": "keep-first",
"next": "their audit notice sends once you clear the invoice"
}
}
]The owner answers with POST /{namespace}/tasks/{task}/claim (Take this one), POST /{namespace}/tasks/{task}/return with a reason, POST /{namespace}/actions/{action}/redirect, POST /{namespace}/approvals/{approval}/approve or POST /{namespace}/approvals/{approval}/refuse, each a one-call window; an Approval unanswered by its deadline expires and the plain notice sends instead. Once a person claims, the agent’s pending notices to that Customer cancel with claimedByPerson (Not live yet, gaps #7). A Task nobody claims by its due time goes up a Role through POST /{namespace}/tasks/{task}/escalate, and person-promise fires.
On time: 4 of 4 claimed within their due time, median 3 h 10 min, n 4, ■□□□ Too early to tell beside Other Startups’ 93 % on time. Your queue: in week 1, 5 approvals and 1 hand-off; after the promotion, about 1 approval (every-year terms) and 1 hand-off a fortnight.
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, gaps #8) | a notice to a Customer whose open Deal a person owns, or whose Conversation a person claimed | Leave it to the person who owns it: the draft waits in their queue. | POST /{namespace}/tasks/{task}/assign |
keep-first (rule j, gaps #8) | a notice to a Customer who is at risk, past due or has an unsubscribe pending | Let their CSM reach them first, then ask again once they are healthy. | POST /{namespace}/tasks/next |
customer-ask-cap (gaps #3) | a second open ask to one Customer in a window, from any Play | usage-upsell holds them until Nov 3. | GET /{namespace}/decisions |
capacity (the record’s Capacity gate) | a notice for an audit the delivery queue cannot finish before its due date | Add delivery capacity, or move the row’s lead time earlier. | PATCH /{namespace}/offers/{id} |
fact-check (Not live yet) | a notice line no record backs, such as a consequence past the approved line | Cut the claim, or name the record that backs it. | POST /{namespace}/content/draft |
| Stop (pauses the Play) | Pauses when | Then |
|---|---|---|
wrong-date | a sent due date differs from the date the map computes from the delivered Order | the row pauses and drops to You approve each one |
misquote | a sent Price or total differs from the live Price | the Play pauses; You approve each one |
already-handled | a notice sends for an outcome already ordered or answered done elsewhere | the row pauses; POST /{namespace}/tasks for you |
refund | refunds or disputes over 5 % of a row’s Orders in 30 days, after 20 | the row pauses |
acceptance-floor | under 3 Orders per 100 notices on a row, after 100 | the row stops until the next readout |
holdout-integrity | a notice reaches a held-back Customer; over 5 % voids the cohort’s readout | POST /{namespace}/tasks/{task}/assign |
kill-switch | you press it | every notice stops; every-year charge notices and cancel links keep sending |
unsubscribe | unsubscribes over 1 % of a week’s notices on a row | that row’s notices pause |
complaint | complaints near 0.1 % of notices | notices pause on every Mailbox |
spend-limit (the record’s Print Limit) | print spend reaches its $100 monthly Limit | printed notices hold; POST /{namespace}/limits/{limit}/raise or wait for the month; email still sends |
The captured refusal, then the Stop that fired as a Campaign.paused reason (Not live yet, gaps #2); the rewrite that sent is the Maple Court thread in Example.
| Check | Fix | Then |
|---|---|---|
fact-check fail · Not live yet, gaps #1 | Cut the claim, or name the record that backs it. | POST /{namespace}/content/draft |
keep-first pass · Not live yet, gaps #8 | — | POST /{namespace}/tasks/next |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "The late note to Maple Court did not send: it states a consequence the outcome map does not approve.",
"checks": [
{
"check": "fact-check",
"status": "fail",
"doc": "/plays/next-outcome#check-fact-check",
"fix": "Cut the claim, or name the record that backs it.",
"then": "Content.draft"
},
{
"check": "keep-first",
"status": "pass"
}
]
}It says no, says why, and names the fix.
[
{
"reason": {
"limit": "spend-limit",
"observed": "$100.40",
"bound": "$100",
"window": "October"
},
"doc": "/plays/next-outcome#stop-spend-limit" ,
"task": "task_466"
}
]Did it work
At 7 due a month, no held-back half reads within 90 days, so the basis is before-after (rule o): the columns read Now and Before, and nothing prints as Added. Each figure’s call is POST /{namespace}/metrics/query by node, and every node above the leaf is Not live yet (gaps #4).
50 per 100 against 19 before · before-after,
- Chose
- send-due-notice
- Why
- Cedar Point Rentals’ Renewal audit was delivered 2025-11-18, so the next is due 2026-11-18; the map row is approved, and $450 is over the $100 print line
- From
- send-due-notice · offer-every-year · late-note · to-the-owner · not-now
- Expects
- Order.placed for the next Renewal audit by 2026-12-18
- Reopens when
- no Order.placed for customer_23’s next audit by 2026-11-21, and the row allows a late audit
- By
- worker_5
JSON
{
"$id": "decisions/decision_455",
"$type": "Decision",
"choice": "send-due-notice",
"reason": "Cedar Point Rentals’ Renewal audit was delivered 2025-11-18, so the next is due 2026-11-18; the map row is approved, and $450 is over the $100 print line",
"stakes": "low",
"options": [
"send-due-notice",
"offer-every-year",
"late-note",
"to-the-owner",
"not-now"
],
"by": "worker_5",
"on": "customer_23",
"expected": "Order.placed for the next Renewal audit by 2026-12-18",
"reopensWhen": "no Order.placed for customer_23’s next audit by 2026-11-21, and the row allows a late audit"
}| Node | Now (56 days) | Before (the 90 days, no notice) | Added | Confidence | Basis |
|---|---|---|---|---|---|
next-outcome-acceptance | 50 per 100: 3 of 6 matured ordered by the due date + 30 days · 8 still maturing · Booked $2,250 in 5 Orders: credited, not all caused by this Play | 19 per 100: 3 of 16 | — · Worth about +$8,200 ARR held | — | before-after, not proof · Other Startups’ results +22 per 100, ■■■□ Likely, n 4,100 |
reactivated-arr ↑ | $17,550 at Oct 19: 3 Orders in the last 28 days × $450 × 13 | $5,850: 1 Order in its last 28 days | — | — | before-after; each $5,850 rolls off 28 days after its Order (one-time-rolloff-arr) |
net-new-arr · arr | carried from reactivated-arr | carried | — | — | carried |
Before against after reads the outcomes that came due in this window against the 16 that came due in the 90 days before it with no notice; it is not proof, and Other Startups’ results print beside it.
How it moves it: each notice reached the Customer 30 days before the due date their own Order set, where before the date passed with nothing sent (next-outcome-acceptance by days to due).
Guardrails: before against after: contraction-arr, $0 from audit Customers who also pay for Pro against $0 before, and refunds, 0 of 5 Orders against a 5 % Stop.
{
"$id": "https://api.sb/leaseline.app/campaigns/next-outcome" ,
"$type": "Campaign",
"play": "play:next-outcome@1" ,
"path": [
"next-outcome-acceptance",
"reactivated-arr",
"net-new-arr",
"arr"
],
"experiment": {
"metric": "next-outcome-acceptance",
"unit": "per-100",
"heldBack": 0,
"decision": null
},
"readout": {
"value": null,
"n": 6,
"maturing": 8,
"interval": null,
"readability": "inadmissible",
"basis": "before-after",
"pooled": true
},
"ledger": {
"spend": 6,
"workerCost": 1,
"hours": 1.1,
"pipelineArr": 900,
"wonArr": 17550,
"projectedArr": 8200,
"paybackDays": 1
}
}Outside benchmark: the catalog holds none for this Play yet, so Other Startups’ results stand in: +22 Orders per 100 due, ■■■□ Likely, n 4,100.
Before it · After it. Before it: Renewal audits reach it through Order.delivered, most of them sold by instant-response and paid-pilot. After it: annual-prepay hears the Customers who took every-year terms, and customer-referrals hears the Customers who ordered twice; keep-first holds both while a Customer is at risk. An offer from your other Startups goes through cross-sell, never this notice.
Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days): 14 Decisions on 13 Customers whose next audit came due, 6 of them past their due date + 30 days. Read every captured thread at ?threads=; three follow the tables.
| What happened, by option | Decisions | Ordered | Booked (credited, not all caused by this Play) |
|---|---|---|---|
| Send the due notice | 7 | 3, 2 still maturing | $1,350 in 3 Orders |
| Offer every-year terms | 2 | 2 on every-year terms | $900 in 2 Orders |
| Send the late note · To the owner · Not now | 2 · 1 · 2 | 0 · 1 Quote open · 0 | $900 Quote open |
| At 3 due a month (the Use it when volume) | about 6 in 56 days | before-after, not proof | Other Startups’ results: +22 per 100, ■■■□ Likely, n 4,100 |
| Day | Event | What happened | Your time |
|---|---|---|---|
| 0 | event_119 Campaign.created | the three steps; the dry run fails billing-connected | 14 min |
| 0 | event_120 Integration.connected | Stripe connected; the first Order sync took 8 min | 5 min |
| 3 | event_177 Order.placed | the first next audit ordered: Juniper Homes, in one click | — |
| 15 | event_388 Role.updated | promoted: due notices send after a 2-minute hold | — |
| 28 | event_532 Change.merged | a reminder on the due date itself: replayed at 58 per 100 | 5 min |
| 39 | event_640 Campaign.paused | spend-limit: print spend reached $100 on Oct 2; printed notices held to Nov 1 | 2 min |
| 1–56 | event_142 Approval.created, first of 9 | you approved 9 notices and 2 every-year terms; weekly reviews, from the POST /{namespace}/tasks/next log | 24 min · total 1.1 h measured, 1 h asked |
Leaseline → Juniper Homes’ billing owner, Aug 27, 07:02 · Your Renewal audit was delivered on Sep 25 last year, so the next is due Sep 25.¹ It is $450, as on our pricing Page.² Book it in one click, or tell us it’s done elsewhere.³ This is an offer from Leaseline. *Signed with the Role’s disclosure line.*⁴
Juniper, Aug 27, 09:15 · pressed Book it.
Order.placed, $450; the audit was delivered Sep 22.
- Your audit is overdue, and late renewal notices could cost you tenants this winter.
+ Your audit was due Oct 9. It can still be done this month at $450; book it in one click, or tell us it’s done elsewhere. Birchway Properties, 2026-10-08 16:20 · “Can you add our 2 new owners to the same audit?” · Leaseline, 16:22 · *Thanks. Priya, your account lead, replies by tomorrow 16:20 with a Quote.*⁵ The Brief went to Priya (
task_471); she claimed it at 17:05 and sent the Quote the next morning at 10:40.
¹ The date the outcome map computes from their own delivered Order. ² The live Price. ³ The one-click Order and the done-elsewhere link. ⁴ The fixed signature line, compiled from the Role’s disclosure Setting. ⁵ The routed line, compiled from the Role’s Settings.
Your settings
---
holdout: 0 # 7 due a month is too few to hold some back; it reads before-after
---
Tell them 30 days before their next Renewal audit is due; mail a printed notice too.
Remind them on the due date itself.
If they ask to change the scope or the price, bring in our AE. $ npx startup-builder show plays/next-outcome
holdout 0 · lead time 30 days · due-date reminder · AE for scope and price your file plays/next-outcome.mdx:2,4,5,6 (Ask 2026-08-24; line 5 by change_29, 2026-09-21)
family keep · worker role:success-manager, owns never · chooses 5 (2 rule, 3 read) · sequences next-outcome-due-notice, next-outcome-overdue-note, next-outcome-yearly-notice · checks owned-by-a-person, keep-first, customer-ask-cap, capacity, fact-check · stops 10 · placements gmail, outlook + postal-mail under a $100 print Limit catalog play:next-outcome@1 (Not live yet, gaps #5) Every key from the catalog
Compiled, not written: edit the Play record next-outcome 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.