Skip to content

When someone cancels: the cancel, then one save offer

When someone cancels: the cancel runs at once, one save offer follows, and no agent can hold it.

A cancel runs the moment the Customer asks. One save offer follows while access lasts, and no agent can hold the cancel.

Cancel, then one save offerMode try
You, to your agent

Tidewater Homes clicked cancel. Let it stand, then make one save offer. Don’t send.

curl -X POST "https://api.sb/leaseline.app/subscriptions/subscription_15/unsubscribe?mode=try" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason":"too-expensive","comment":"We only manage 12 units now."}'

curl -X POST "https://api.sb/leaseline.app/subscriptions/subscription_15/keep?mode=try" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"offer":"offer_5"}'

curl -X POST "https://api.sb/leaseline.app/actions/action_702/redirect?mode=try" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"changes":{"effectiveAt":"2026-12-14"}}'

curl -X POST https://api.sb/leaseline.app/metrics/query \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"metric":"save-rate","shape":"trend","window":"28d"}'
import { $ } from 'startup-builder'

await $.Subscription.unsubscribe({ subscription: 'subscription_15', reason: 'too-expensive', comment: 'We only manage 12 units now.' }, { mode: 'try' })   // 200: it ends Nov 14, at the close of the period, and access lasts until then
await $.Subscription.keep({ subscription: 'subscription_15', offer: 'offer_5' }, { mode: 'try' })   // the one save offer: the $19 Starter plan for 12 units, theirs to press
await $.Action.redirect({ action: 'action_702', changes: { effectiveAt: '2026-12-14' } }, { mode: 'try' })   // an agent tries to hold the cancel a month
// 422 · The cancel for Tidewater Homes was not held: a cancel runs on its date.
// exit-never-waits: Leave the cancel standing; the Customer takes it back with one press while access lasts. · then Subscription.keep
return await $.Metric.query({ metric: 'save-rate', shape: 'trend', window: '28d' })
npx startup-builder do Subscription.unsubscribe --json '{"subscription":"subscription_15","reason":"too-expensive","comment":"We only manage 12 units now."}' --try
npx startup-builder do Subscription.keep --json '{"subscription":"subscription_15","offer":"offer_5"}' --try
npx startup-builder do Action.redirect --json '{"action":"action_702","changes":{"effectiveAt":"2026-12-14"}}' --try
npx startup-builder do Metric.query --json '{"metric":"save-rate","shape":"trend","window":"28d"}'
{
  "name": "do",
  "arguments": {
    "code": "await $.Subscription.unsubscribe({ subscription: 'subscription_15', reason: 'too-expensive', comment: 'We only manage 12 units now.' }, { mode: 'try' })   // 200: it ends Nov 14, at the close of the period, and access lasts until then\nawait $.Subscription.keep({ subscription: 'subscription_15', offer: 'offer_5' }, { mode: 'try' })   // the one save offer: the $19 Starter plan for 12 units, theirs to press\nawait $.Action.redirect({ action: 'action_702', changes: { effectiveAt: '2026-12-14' } }, { mode: 'try' })   // an agent tries to hold the cancel a month\n// 422 · The cancel for Tidewater Homes was not held: a cancel runs on its date.\n// exit-never-waits: Leave the cancel standing; the Customer takes it back with one press while access lasts. · then Subscription.keep\nreturn await $.Metric.query({ metric: 'save-rate', shape: 'trend', window: '28d' })",
    "mode": "try"
  }
}
Response200application/json
{
  "metric": {
    "$id": "https://api.sb/leaseline.app/metrics/save-rate",
    "$type": "Metric",
    "name": "Save rate"
  },
  "rows": [
    {
      "week": "2026-10-19",
      "value": 0.33,
      "saved": 1,
      "cancels": 3
    }
  ]
}
sample

1 of 3 cancels saved in 28 days · ■□□□ Too early to tell · n 3

Metric.query({"metric":"save-rate","shape":"trend","window":"28d"}) · sample · leaseline.app at 2026-10-19

What it does

What it doesPrice per unitMovesWho does itToday
POST /{namespace}/subscriptions/{subscription}/unsubscribe ends the Subscription at the close of its period and returns 200; one POST /{namespace}/subscriptions/{subscription}/keep offer follows, the Customer’s own press.$0 per cancel; $0.04 per save note (Worker cost)Save rate, Lost CustomersCSMevery call live

Set it up

Check (dry run on a 0-Customer Startup, gaps #8)FixThenYour timeWait
billing-connected (Not live yet, gaps #8) · failConnect Stripe so every charge and Meter reading is live.POST /{namespace}/integrations/connect5 minthe first sync, about 10 min
role-unstaffed (Not live yet, gaps #8) · warn: the CSM has no person: save offers come to you to approveStaff the CSM, or leave it: each save offer comes to you.POST /{namespace}/roles/{role}/staff0 minnone

The records

Tidewater Homes, after the cancel
{
  "$id": "https://api.sb/leaseline.app/subscriptions/subscription_15",
  "$type": "Subscription",
  "status": "active",
  "renewsAt": null,
  "termEndsAt": "2026-11-14",
  "quantity": 1
}

Calls

CallHereEmitsStatus
POST /{namespace}/subscriptions/{subscription}/unsubscribe200: it ends Nov 14, at the close of the period, and access lasts until thenSubscription.unsubscribedlive
POST /{namespace}/subscriptions/{subscription}/keepthe one save offer: the $19 Starter plan for 12 units, theirs to pressSubscription.keptlive
POST /{namespace}/actions/{action}/redirectan agent tries to hold the cancel a monthAction.redirectedlive
POST /{namespace}/metrics/queryMetric.queriedlive

Events

EventWhen it firesWhat hears it
Subscription.unsubscribedthe Customer cancels, at oncethe save step of save-and-win-back, and the reason to customer-research
Subscription.keptthe Customer presses keep before Nov 14the CSM’s readout; nothing is booked as lost

Refuses when

422RefusedThe cancel for Tidewater Homes was not held: a cancel runs on its date.
CheckFixThen
exit-never-waits fail · Not live yet, gaps #3Leave the cancel standing; the Customer takes it back with one press while access lasts.POST /{namespace}/subscriptions/{subscription}/keep
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The cancel for Tidewater Homes was not held: a cancel runs on its date.",
  "checks": [
    {
      "check": "exit-never-waits",
      "status": "fail",
      "doc": "/code/refusals#check-exit-never-waits",
      "fix": "Leave the cancel standing; the Customer takes it back with one press while access lasts.",
      "then": "Subscription.keep"
    }
  ]
}

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

CheckRefusesFixThen
exit-never-waits (Not live yet, gaps #3)any agent Action that holds, delays or conditions a Customer’s cancel or downgradeLeave the cancel standing; the Customer takes it back with one press while access lasts.POST /{namespace}/subscriptions/{subscription}/keep
customer-ask-cap (Not live yet, gaps #3)a second open ask to one Customer in a window, from any PlayWait for the open ask to close; GET /{namespace}/decisions names the Play that holds them.GET /{namespace}/decisions

Plays that use it

PlayWhat it doesUse it when
Annual prepayOffers a steady monthly Customer a year up front, at a discount their own churn pays for, and answers an annual cancel with one save.10 or more monthly Customers past their third renewal, losing 2 % or more a month
save-at-cancel (Not live yet, gaps #4)The Play that offers one save after a Customer cancels, with the cancel already done.today: The hub Workflow cancel-subscription, then one POST /{namespace}/subscriptions/{subscription}/keep offer.

Hub guides it deepens

On startupbuilder.devId
Cancel a subscriptionworkflow:cancel-subscription
Save and win backworkflow:save-and-win-back

Example

Leaseline, as the fixture ran it
Tidewater Homes · cancel on Oct 18, reason too-expensive, “We only manage 12 units now.” · 200, ends Nov 14 · one save offer: the $19 Starter plan · an agent’s hold on the cancel refused (exit-never-waits) · 1 of 3 cancels saved in 28 days · sample

Compiled, not written: edit its page, 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.

Contents