URL: /plays/next-outcome · Id: `play:next-outcome`

# 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.

Tells a [Customer](https://startupbuilder.dev/business/nouns/customer) the day their last order makes the next one due, with the live price and a one-click order.

```text title="Set it up · Ask"
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.
```

```text title="Run one · Ask"
Hand our latest Customer whose next audit came due to the CSM agent and show me what it chose and why. Don’t send.
```

```ts title="Run one · SDK"
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 }
```

```json title="Run one · Response · sample"
{
  "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"
    ],
    "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"
  },
  "actions": [
    {
      "$id": "https://api.sb/leaseline.app/actions/action_1",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_5",
      "verb": "Order.get",
      "checks": []
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_2",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_5",
      "verb": "Content.draft",
      "checks": [
        {
          "check": "fact-check",
          "status": "pass"
        }
      ]
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_3",
      "$type": "Action",
      "status": "pending",
      "reason": "awaitingTime",
      "mode": "try",
      "by": "worker_5",
      "verb": "Message.schedule",
      "checks": [
        {
          "check": "owned-by-a-person",
          "status": "pass"
        },
        {
          "check": "keep-first",
          "status": "pass"
        },
        {
          "check": "customer-ask-cap",
          "status": "pass"
        },
        {
          "check": "capacity",
          "status": "pass"
        }
      ]
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_4",
      "$type": "Action",
      "status": "pending",
      "reason": "awaitingTime",
      "mode": "try",
      "by": "worker_5",
      "verb": "Document.render",
      "checks": []
    }
  ]
}
```

`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, not proof · n 6 · path → arr`

| 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 |

| Worth | Your time | Spend | First signal | Use it when | Instead |
| --- | --- | --- | --- | --- | --- |
| ≈ +$8,200 ARR held, at 7 due a month | 30 min week 1, then 5 a week | $0.05 per notice, ~$7 a month at 7 with print | Day 3: first next audit ordered | 10+ Service Customers, 3+ next outcomes due a month | Under 3 due a month: `renewal-notice` · no outcome map: map it (<Op verb="Offer.update" />) |

**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: 0` and reads before-after.

`sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Schedule.fired · first notice 2m 40s after the lead time opened · 9 keys Not live yet`

## Start it this week
```bash
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"}'
```


```json title="The dry run’s 422 · 2026-08-24 · sample"
{
  "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"
    }
  ]
}
```

| Check | Fix (one call) | Your time | Wait |
| --- | --- | --- | --- |
| <a id="check-use-when"></a>`use-when` · pass: 22 Customers, 7 due a month (gaps #8) | <Op verb="Metric.query" /> on `next-outcome-acceptance` | 1 min | none |
| <a id="check-billing-connected"></a>`billing-connected` · fail | <Op verb="Integration.connect" /> with `system` recurring-billing (Stripe) | 5 min | the first Order sync, about 10 min |
| <a id="check-baseline"></a>`baseline` · warn: 3 of 16 in 90 days | <Op verb="Metric.query" />; it fills Before in Your number | 0 min | none |
| <a id="check-owned-by-a-person"></a>`owned-by-a-person` · <a id="check-keep-first"></a>`keep-first` · <a id="check-customer-ask-cap"></a>`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 |
| <a id="check-role-unstaffed"></a>`role-unstaffed` · warn: 2 of your 22 have a person owner, you | <Op verb="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, not proof · n 6 matured · Other Startups +22, ■■■□ Likely`

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; <Op verb="Order.deliver" /> writes the date |
| You deliver a Renewal audit | `service-order-delivered` | `event` on `Order.delivered` where offer.kind = 'service' | 1d | <Op verb="Order.deliver" />, or <Op verb="Integration.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=all`) | | | | |

```json title="One signed delivery · StartupBuilder-Signature"
{
  "$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"
  }
}
```

```bash title="Reproduce it in test"
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:success-manager`, `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 | <Op verb="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 | — | <Op verb="Approval.approve" /> · <Op verb="Approval.refuse" /> |
| To the owner | It does it | 1 of 1 | — | a held Customer is asked anyway → You approve each one | <Op verb="Action.redirect" /> |
| Change the outcome map | You approve each one | 1 merged of 1 | never | — | <Op verb="Change.merge" /> · <Op verb="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. <Op verb="Order.place" /> is the Customer’s own press on the one-click Page, never the agent’s call. Its tools are <Op verb="Message.schedule" />, <Op verb="Approval.create" />, <Op verb="Conversation.escalate" />, <Op verb="Task.assign" />, and any other call becomes an Approval under the Role’s Mandate.

```md title="The brief (folded): compiled Agent-register Markdown · play:next-outcome@1 · each line names its record key"
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 7
```

**The 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 <Op verb="Change.merge" />. **Replayed on past notices**, <Op verb="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; <Op verb="Role.staff" /> seats it.

```bash
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 → <Op verb="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 → <Op verb="Approval.create" />; 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 | <Op verb="Conversation.escalate" /> → <Op verb="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 | — | <Op verb="Order.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 | <Op verb="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 | <Op verb="Decision.list" /> |
| 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 | <Op verb="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 | <Op verb="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 | <Op verb="Conversation.escalate" /> |
| Stop | “Stop sending these.” | notices stop on every lane; a yearly charge notice and its cancel link still send | nobody | at once | <Op verb="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).

```json title="What lands in each queue · sample"
[
  {
    "$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 <Op verb="Task.claim" /> (Take this one), <Op verb="Task.return" /> with a reason, <Op verb="Action.redirect" />, <Op verb="Approval.approve" /> or <Op verb="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 <Op verb="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:** <Op verb="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. | <Op verb="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. | <Op verb="Task.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. | <Op verb="Decision.list" /> |
| <a id="check-capacity"></a>`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. | <Op verb="Offer.update" /> |
| <a id="check-fact-check"></a>`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. | <Op verb="Content.draft" /> |

| Stop (pauses the Play) | Pauses when | Then |
| --- | --- | --- |
| <a id="stop-wrong-date"></a>`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 |
| <a id="stop-misquote"></a>`misquote` | a sent Price or total differs from the live Price | the Play pauses; You approve each one |
| <a id="stop-already-handled"></a>`already-handled` | a notice sends for an outcome already ordered or answered done elsewhere | the row pauses; <Op verb="Task.create" /> for you |
| <a id="stop-refund"></a>`refund` | refunds or disputes over 5 % of a row’s Orders in 30 days, after 20 | the row pauses |
| <a id="stop-acceptance-floor"></a>`acceptance-floor` | under 3 Orders per 100 notices on a row, after 100 | the row stops until the next readout |
| <a id="stop-holdout-integrity"></a>`holdout-integrity` | a notice reaches a held-back Customer; over 5 % voids the cohort’s readout | <Op verb="Task.assign" /> |
| <a id="stop-kill-switch"></a>`kill-switch` | you press it | every notice stops; every-year charge notices and cancel links keep sending |
| <a id="stop-unsubscribe"></a>`unsubscribe` | unsubscribes over 1 % of a week’s notices on a row | that row’s notices pause |
| <a id="stop-complaint"></a>`complaint` | complaints near 0.1 % of notices | notices pause on every Mailbox |
| <a id="stop-spend-limit"></a>`spend-limit` (the record’s Print Limit) | print spend reaches its $100 monthly Limit | printed notices hold; <Op verb="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.

```json title="The captured refusal · sample"
{
  "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"
    }
  ]
}
```


```json title="The Stops that fired, as Campaign.paused reasons · sample"
[
  {
    "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 <Op verb="Metric.query" /> by node, and every node above the leaf is Not live yet (gaps #4).

`50 per 100 against 19 before · before-after, not proof · n 6 · path → arr`

| 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.

```json title="The Campaign card · sample"
{
  "$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=all`; 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 <Op verb="Task.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.

```diff title="Maple Court, 2026-10-12 · the late note fact-check refused, then the rewrite that sent"
- 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
```mdx title="plays/next-outcome.mdx"
---
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.
```

```text
$ 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
| Key | Id | What it is |
| --- | --- | --- |
| Tactic | <a id="tactic-outcome-map-keep"></a>`outcome-map-keep` | Outcome map keep: Keeps the founder-approved map from each Service Offer to the outcomes it leads to, with each due rule, lead time and consequence line. Starts at Sends nothing. |
| Tactic | <a id="tactic-next-outcome-plan"></a>`next-outcome-plan` | Next outcome plan: Turns each delivered Service Order into dated next outcomes on the Customer’s own account, skips any already handled, and assigns the holdout. Starts at Sends nothing. |
| Tactic | <a id="tactic-next-outcome-notice"></a>`next-outcome-notice` | Next outcome notice: Sends one due notice from the Startup’s Mailbox at the lead time: the date, the live Price, a one-click Order and a done-elsewhere link. Starts at You approve each one. |
| Tactic | <a id="tactic-next-outcome-order"></a>`next-outcome-order` | Next outcome order: Turns a press on the notice into a prefilled Stripe Checkout Order, offers every-year terms at the same Price, and hands it to delivery. Starts at Sends nothing. |
| Tactic | <a id="tactic-next-outcome-readout"></a>`next-outcome-readout` | Next outcome readout: Reads acceptance per outcome against its holdout every 90 days, runs the decay check every 30 days, and flags weak outcomes for the founder. Starts at Sends nothing. |
| Tactic | <a id="tactic-reply-matrix"></a>`reply-matrix` | Reply matrix: Labels every outreach reply with one of 11 labels, maps it to one of seven reply kinds, and runs that kind’s move by its deadline. Starts at You approve each one. |
| Tactic | <a id="tactic-sending-stop-loss"></a>`sending-stop-loss` | Sending stop loss: Reads the Play’s own bounces, replies and seed tests on every send, and rests, pauses or retires a Mailbox, Domain, cell or the Play. Starts at Sends nothing. |
| Trigger | <a id="trigger-service-order-delivered"></a>`service-order-delivered` | Service order delivered: `event` on `Order.delivered`, window 1d. |
| Trigger | <a id="trigger-next-outcome-due"></a>`next-outcome-due` | Next outcome due: `at` on `Schedule.fired`, window 30d. |
| Trigger | <a id="trigger-next-outcome-overdue"></a>`next-outcome-overdue` | Next outcome overdue: `state` on `Schedule.fired`, window 14d. |
| Trigger | <a id="trigger-next-outcome-ordered"></a>`next-outcome-ordered` | Next outcome ordered: `event` on `Order.placed`, window 1d. |
| Trigger | <a id="trigger-delivered-order-reversed"></a>`delivered-order-reversed` | Delivered order reversed: `event` on `Order.refunded`, window 1d. |
| Trigger | <a id="trigger-outcome-map-changed"></a>`outcome-map-changed` | Outcome map changed: `event` on `Offer.published`, window 7d. |
| Trigger | <a id="trigger-done-elsewhere-reply"></a>`done-elsewhere-reply` | Done elsewhere reply: `event` on `Message.received`, window 3d. |
| Trigger | <a id="trigger-yearly-charge-due"></a>`yearly-charge-due` | Yearly charge due: `at` on `Renewal.opened`, window 30d. |
| Sequence | <a id="sequence-next-outcome-due-notice"></a>`next-outcome-due-notice` | Next outcome due notice: 4 steps; it ends on An Order for the outcome, here or at a sibling Startup; A done-elsewhere answer; An unsubscribe or a Suppression; A refund or dispute on the delivered Order; The due date passing. |
| Sequence | <a id="sequence-next-outcome-overdue-note"></a>`next-outcome-overdue-note` | Next outcome overdue note: 1 steps; it ends on An Order for the outcome; A done-elsewhere answer; An unsubscribe or a Suppression; 14 days passing. |
| Sequence | <a id="sequence-next-outcome-yearly-notice"></a>`next-outcome-yearly-notice` | Next outcome yearly notice: 2 steps; it ends on A cancel; The charge. |