Skip to content

Saved renewals

At-risk accounts kept through their renewal, billed only for each one still paying 30 days past its renewal date.

Kind
Service
Price
$120 per saved renewal
Your time
25 minutes
Approval
Runs on its own
Deepens
Retention
Default limits
What waits for you
More factsFewer facts
Renewal.save
You, to your agent

Save this month’s at-risk renewals, $120 for each one still paying 30 days later, and show me the plan first.

curl -X POST "https://api.sb/leaseline.app/renewals/save?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"goal":"Save this month’s at-risk renewals, $120 for each one still paying 30 days later, and show me the plan first.","budget":{"amount":1080,"currency":"USD"}}'
npx startup-builder do Renewal.save --try --data '{"goal":"Save this month’s at-risk renewals, $120 for each one still paying 30 days later, and show me the plan first.","budget":{"amount":1080,"currency":"USD"}}'
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.Renewal.save({ goal: 'Save this month’s at-risk renewals, $120 for each one still paying 30 days later, and show me the plan first.', budget: { amount: 1080, currency: 'USD' } })"
  }
}
import { $ } from 'startup-builder'

const result = await $.try.Renewal.save({ goal: 'Save this month’s at-risk renewals, $120 for each one still paying 30 days later, and show me the plan first.', budget: { amount: 1080, currency: 'USD' } })
Response
{
  "mode": "try",
  "stored": false,
  "estimate": "About 7¢ per 10 flagged accounts",
  "bills": "$120 a saved renewal",
  "sample": {
    "account": "Willow Bend Rentals",
    "flagged": "usage down 46% over 4 weeks",
    "offer": "60-day pause",
    "renewed": "February 14",
    "state": "paying at day 30, accepted"
  }
}
Try mode reads, stores nothing and spends nothingAPI referenceIts Nouns

What you get

  • 10 flagged accounts, as Subscription records
  • saved[] · Each account still paying 30 days past its renewal: the clause that flagged it, the offer it took, and the payment that cleared.
  • lost[] · Each flagged account that cancelled, with its reason; none is billed.
  • bill · Accepted saves times the price.

Connect once: Stripe, Gmail. How long: 30 days before each renewal to 30 days after it.

Trigger

It runs when you call it, or on any Trigger you set.

TypeScript
await $.Workflow.create({
  verb: 'Renewal.save',
  trigger: { schedule: 'weekly' }
})

Steps

Your agent does these steps, one row per step; each step’s check is part of its row and its price.

StepVerbReturnsPriceApproval
1. List the renewals due in 30 daysCollection.queryEvery subscription renewing inside the horizon, from StripeFreeRuns on its own
2. Score each one by the risk rule, dailyCustomer.scoreEach account at risk or not, with the clauses that fired, before the save note goesFreeRuns on its own
3. Send each flagged account one save noteCustomer.saveOne note with their own numbers and the offer your Policy allows, within 24 hours of the flagAbout 7¢Runs on its own
4. Open the renewal on the saved termsRenewal.openThe renewal set on the offer the account took: a pause, a downgrade or a creditFreeRuns on its own
5. Read the payment 30 days past the renewalCollection.queryEach saved account paying when its renewal payment cleared and no refund followed in 30 daysFreeRuns on its own

Calls: Collection.query, then Customer.score, then Customer.save, then Renewal.open, then Collection.query.

About 7¢ per 10 flagged accounts, each time it runs, capped at 50¢. Its own steps cost nothing; the compositions it orders add 7¢.

How it runsWhat it says
Count only accounts the rule flagged firstA save bills only when your declared risk rule flagged the account before the note went. An account that was renewing anyway is never a save.
Price it against replacing the customerReplacing a lost $149-a-month customer costs up to the $795 that plan can afford to acquire one. $120 is 15% of that.
Read paying at day 30, not a promiseA reply that says “we will stay” bills nothing. The renewal payment clearing, with no refund in 30 days, does.

Example: give it a goal

Save this month’s at-risk renewals, $120 for each one still paying 30 days later, and show me the plan first.

The plan it shows runs 5 steps, priced before it spends anything; Run it above shows the result it returns.

When something fails

Each failure, and what happens next
WhenThen
The best save is a discount past your PolicyIt is a price action and waits for you by the seat rule on price; the 24 hours pause while it waits.
A flagged account has an open support conversationIt is handed to a person with the context, never offered a deal over an open issue.
A saved account disputes the renewal chargeThe save is credited and the dispute is handed to Billing.

Each step retries three times, then falls through to the next route; one item's failure lands in skipped with its reason, never failing the call. Before a step would cross maxCost it stops and returns what it has, with a resume link. Each step's idempotency key is the call's key plus the step and the item: leaseline-renewal-save-1-<step>-<item>.

What it promises

TermThis Service
DoneAn account your risk rule flagged before its renewal date is still paying 30 days after that date.
Counted byRenewal.saved
Billed$120 a saved renewal, on acceptance
TargetEvery flagged account renewing in the month.
Accepted whenThe renewal payment clearing with no refund in 30 days accepts it.
CreditedAn account that refunds or disputes the renewal payment inside 30 days.
RemedyA flagged account that cancels is never billed, and its reason is written to the customer for the next rule.
EvaluatorCode reads the flag time from Silence watch and the renewal invoice and any refund from Stripe.
Window30 days before each renewal date to 30 days after it.
Assurancemeasured
Oversightnone: agents and code do every step

You pay $120 for each saved renewal you accept. Behind it, the agents spend about 7¢ in calls for 10 flagged accounts, never more than 50¢, shown beside your bill. That spend is inside the $120: you are billed for accepted units only, never for the calls.

February: 31 Leaseline renewals due, 9 flagged at risk.

CountWhat happened
9Ordered
9Flagged by the risk rule and sent a save note: 4 on usage, 3 on logins, 2 on a failed payment.
6Still paying 30 days past renewal: 3 on a pause, 2 on a downgrade, 1 after the fix.
3Cancelled anyway: never billed, each reason written to the customer.
1Disputed the renewal charge inside 30 days: credited.
5Accepted and billed: 5 × $120 = $600

Change it

AskCompiles to
Flag anyone with no login in 10 days instead of 14.Customer.score
Offer a 90-day pause instead of 60.Customer.save
Send me accounts paying over $500 a month instead of saving them.Renewal.save
TypeScript
import { $, Service } from 'startup-builder'

export default Service({
  name: 'renewal-save',
  verb: 'Renewal.save',
  input: { rule: 'string', horizon: 'string', policy: 'string' },
  maxCost: 0.5,
  // each one's cost rolls up into this call
  orders: ['Customer.score', 'Customer.save'],
  oversight: { posture: 'none' },
  contract: {
    done: 'Renewal.saved', accept: {
      after: 'renewal',
      window: '30d',
      refund: 'none'
    }, assurance: 'measured',
    bills: { usd: 120, per: 'saved renewal' }, credits: [
      'refund-or-dispute-within-30d'
    ], remedy: 'never-bill-a-loss',
  },
  execute: async (ctx, { rule, horizon = '30d', policy }) => {
    const due = await ctx.step(
      'due',
      () => $.Collection.query({
        from: 'subscription',
        where: `renewsAt <= now + ${horizon}`
      })
    )
    const scored = await ctx.step(
      'score',
      () => $.Customer.score({ customers: due.map((s) => s.customer), rule })
    )
    for (const c of scored.atRisk) await ctx.step(
      `save:${c.customer}`,
      () => $.Customer.save({ customer: c.customer, policy })
    )
  },
})

Extend it with extends: 'Renewal.save' and base(), or compose your own from these steps.

Works with

Plays that order it: Annual prepay, Next order due.

OrdersKindRuns, each at its own priceRolls up
Silence watchWorkflow30 × FreeFree
Save and win backAgent task10 × About 0.7¢ a customerAbout 7¢

It serves Keep and grow customers. It deepens Retention.

For agents

For agents: the guide, its approvals, retries and lookups
FactValue
GuideRenewal.save
Guide approvalNone.
GoalSave this month’s at-risk renewals, $120 for each one still paying 30 days later, and show me the plan first. It may change counts, the order of steps, soft filters and the route a read takes, and it stops early when the goal is met. It never relaxes your words, raises its own budget, answers its own approval, calls a Play or acts outside your standing approval.
RetryThree times with backoff, then the next route; the same idempotency key never charges, sends or posts twice
Idempotency keyleaseline-renewal-save-1
LookupsA find: input asks unless exactly one record fits

Nothing here waits for you: every step reads, or writes only your own records, up to the 50¢ cap.

Who does each step, and how it is checked
StepFunctionDoerCheckerAssuranceInputs
1. List the renewals due in 30 dayscodeCodeCode reads each renewal date from the subscription in Stripemeasured$0.horizon
2. Score each one by the risk rule, daily, by Silence watchcodeCodeCode scores usage by the declared rule and reads each flag backmeasured$1.subscriptions, $0.rule
3. Send each flagged account one save note, by Save and win backagenticThe CSM agentA second model checks each note against the account’s record and your Policygraded$2.atRisk, $0.policy
4. Open the renewal on the saved termscodeCodeCode reads the subscription back from Stripe with the new termsmeasured$3.accepted
5. Read the payment 30 days past the renewalcodeCodeCode reads the renewal invoice and any refund from Stripe at day 30measured$4.renewals

Code does steps 1, 2, 4 and 5; and the CSM agent does step 3. A second model checks the step it grades, never the one that wrote it, so the whole call is graded.

Under the hood

Under the hood: the Systems, Nouns and Events it touches
KindWhat it touches
SystemsNone.
NounsCollection, Customer, Renewal
EventsCollection.queried, Renewal.opened

Reference

Inputs, outputs, its Event and its errors
NameTypeDefaultMeaning
rulePolicyyour retention PolicyThe declared risk rule. The default: no login in 14 days, usage down 40% over 4 weeks, or a failed payment.
horizonstring'30d'How far ahead it reads renewal dates.
policyPolicyyour retention PolicyWhat a save may offer without asking: a pause up to 60 days, a downgrade, a fix, or a credit up to the Policy’s amount.

Emits one Event named for its Verb. Refuses with max-cost before it spends, and with the Data calls and Accounts groups (refusals).

Contents