Skip to content

Who might cancel: health, silence and who to call first

Who might cancel: health by Customer, 14 days of silence, and the first call your CSM makes.

Customer health reads every paying Customer each morning, and the CSM’s queue opens on the one most likely to cancel.

Who might cancelMode try
You, to your agent

Which customers might cancel, and who do I call first?

curl -X POST https://api.sb/leaseline.app/metrics/query \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"metric":"customer-health","shape":"trend","breakdown":"customer"}'

curl -X POST "https://api.sb/leaseline.app/processes/retention/enable?mode=try" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"

curl -X POST https://api.sb/leaseline.app/tasks/next \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"

curl -X POST "https://api.sb/leaseline.app/messages/message_77/schedule?mode=try" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"at":"2026-10-22T15:00:00Z"}'

# Not live yet (gaps #4)
curl -X POST https://api.sb/leaseline.app/metrics/query \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"metric":"churned-arr","shape":"trend","window":"7d"}'
import { $ } from 'startup-builder'

const { rows } = await $.Metric.query({ metric: 'customer-health', shape: 'trend', breakdown: 'customer' })   // 4 Customers at risk; Harbor Row lowest since Oct 2
await $.Process.enable({ process: 'retention' }, { mode: 'try' })   // silence-watch: 14 days with no sign-in opens a Task
const { task } = await $.Task.next()   // your CSM agent: "Call Harbor Row first: 3 support threads on late notices"
await $.Message.schedule({ message: 'message_77', at: '2026-10-22T15:00:00Z' }, { mode: 'try' })   // the upgrade note usage-upsell drafted for Harbor Row
// 422 · The upgrade note to Harbor Row did not send: they are at risk.
// keep-first: Let their CSM reach them first, then ask again once they are healthy. · then Task.next
return await $.Metric.query({ metric: 'churned-arr', shape: 'trend', window: '7d' })   // Not live yet, gaps #4
npx startup-builder do Metric.query --json '{"metric":"customer-health","shape":"trend","breakdown":"customer"}'
npx startup-builder do Process.enable --json '{"process":"retention"}' --try
npx startup-builder do Task.next
npx startup-builder do Message.schedule --json '{"message":"message_77","at":"2026-10-22T15:00:00Z"}' --try
npx startup-builder do Metric.query --json '{"metric":"churned-arr","shape":"trend","window":"7d"}'   # Not live yet (gaps #4)
{
  "name": "do",
  "arguments": {
    "code": "const { rows } = await $.Metric.query({ metric: 'customer-health', shape: 'trend', breakdown: 'customer' })   // 4 Customers at risk; Harbor Row lowest since Oct 2\nawait $.Process.enable({ process: 'retention' }, { mode: 'try' })   // silence-watch: 14 days with no sign-in opens a Task\nconst { task } = await $.Task.next()   // your CSM agent: \"Call Harbor Row first: 3 support threads on late notices\"\nawait $.Message.schedule({ message: 'message_77', at: '2026-10-22T15:00:00Z' }, { mode: 'try' })   // the upgrade note usage-upsell drafted for Harbor Row\n// 422 · The upgrade note to Harbor Row did not send: they are at risk.\n// keep-first: Let their CSM reach them first, then ask again once they are healthy. · then Task.next\nreturn await $.Metric.query({ metric: 'churned-arr', shape: 'trend', window: '7d' })   // Not live yet, gaps #4",
    "mode": "try"
  }
}
Response200application/json
{
  "metric": {
    "$id": "https://api.sb/leaseline.app/metrics/churned-arr",
    "$type": "Metric",
    "name": "Churned ARR"
  },
  "rows": [
    {
      "week": "2026-10-19",
      "value": -1041,
      "customers": 3,
      "atRiskWeekBefore": 2
    }
  ]
}
sample · 1 Not live yet

−$1,041 ARR lost this week · 3 Customers · 2 of them at risk a week earlier

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

What it does

What it doesPrice per unitMovesWho does itToday
POST /{namespace}/metrics/query reads customer-health by Customer each morning; the silence-watch Workflow opens a Task after 14 days with no sign-in.$0 per read; $0.04 per check-in note (Worker cost)Customer health, Lost CustomersCSM4 of 5 calls 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: at-risk Tasks come to youStaff the CSM, or leave it: at-risk Tasks come to you.POST /{namespace}/roles/{role}/staff0 minnone

The records

CustomerHealthAt risk sinceWhyWho callsBy
Harbor Row31Oct 23 support threads on late noticesCSM · your agent drafts, you callOct 20
Cedar Lane38Oct 9no sign-in for 19 days; asked us to stop emailing on Oct 12you, by phoneOct 22
Juniper Homes44Oct 12use down 40 %, renews Nov 3CSM · POST /{namespace}/renewals/openOct 19
Pinecrest Lofts47Oct 14their admin leftCSM · your agentOct 21
The four at risk, who calls first
{
  "$id": "https://api.sb/leaseline.app/metrics/customer-health",
  "$type": "Metric",
  "name": "Customer health",
  "rows": [
    {
      "customer": "customer_19",
      "value": 31,
      "atRiskSince": "2026-10-02"
    }
  ]
}

Calls

CallHereEmitsStatus
POST /{namespace}/metrics/query4 Customers at risk; Harbor Row lowest since Oct 2Metric.queriedlive
POST /{namespace}/processes/{process}/enablesilence-watch: 14 days with no sign-in opens a TaskProcess.enabledlive
POST /{namespace}/tasks/nextyour CSM agent: "Call Harbor Row first: 3 support threads on late notices"Task.offeredlive
POST /{namespace}/messages/{message}/schedulethe upgrade note usage-upsell drafted for Harbor RowMessage.scheduledlive
POST /{namespace}/metrics/queryMetric.queriedNot live yet, gaps #4

Events

EventWhen it firesWhat hears it
Task.offereda Customer turns at risk, or 14 days pass with no sign-inthe CSM’s POST /{namespace}/tasks/next
Renewal.openeda term Subscription nears its endthe CSM’s renewal Task

Refuses when

422RefusedThe upgrade note to Harbor Row did not send: they are at risk.
CheckFixThen
keep-first fail · Not live yet, gaps #8Let their CSM reach them first, then ask again once they are healthy.POST /{namespace}/tasks/next
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The upgrade note to Harbor Row did not send: they are at risk.",
  "checks": [
    {
      "check": "keep-first",
      "status": "fail",
      "doc": "/code/refusals#check-keep-first",
      "fix": "Let their CSM reach them first, then ask again once they are healthy.",
      "then": "Task.next"
    }
  ]
}

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

CheckRefusesFixThen
keep-first (Not live yet, gaps #8)an upgrade, referral, review or reference ask to a Customer who is at risk, past due or leavingLet their CSM reach them first, then ask again once they are healthy.POST /{namespace}/tasks/next
owned-by-a-person (Not live yet, gaps #8)a send to a Contact whose Lead, open Deal or Conversation a person ownsLeave it to the person who owns it: the draft waits in their queue.POST /{namespace}/tasks/{task}/assign

Plays that use it

PlayWhat it doesUse it when
quiet-account-rescue (Not live yet, gaps #4)The Play that calls a paying Customer who went quiet, before they cancel.today: The hub Workflow silence-watch, which opens a Task in POST /{namespace}/tasks/next.
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
Objection watchRecords why each Customer or Deal said no, holds them from other outreach, and sends one Message, with proof, once the reason stops being true.20 or more nos a month recorded with a reason: lost Deals, cancels, downgrades and stalled trials

Hub guides it deepens

On startupbuilder.devId
Retentionprocess:retention
Silence watchworkflow:silence-watch

Example

Leaseline, as the fixture ran it
Oct 19 · 4 at risk · Harbor Row first (31) · the upgrade note usage-upsell drafted for them is refused (keep-first) · the CSM calls Oct 20 · 2 of the 3 Customers lost this week were at risk a week earlier · 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