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
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' } })
{
"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"
}
}
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.
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.
| Step | Verb | Returns | Price | Approval |
|---|---|---|---|---|
| 1. List the renewals due in 30 days | Collection.query | Every subscription renewing inside the horizon, from Stripe | Free | Runs on its own |
| 2. Score each one by the risk rule, daily | Customer.score | Each account at risk or not, with the clauses that fired, before the save note goes | Free | Runs on its own |
| 3. Send each flagged account one save note | Customer.save | One note with their own numbers and the offer your Policy allows, within 24 hours of the flag | About 7¢ | Runs on its own |
| 4. Open the renewal on the saved terms | Renewal.open | The renewal set on the offer the account took: a pause, a downgrade or a credit | Free | Runs on its own |
| 5. Read the payment 30 days past the renewal | Collection.query | Each saved account paying when its renewal payment cleared and no refund followed in 30 days | Free | Runs 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 runs | What it says |
|---|---|
| Count only accounts the rule flagged first | A 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 customer | Replacing 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 promise | A 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
| When | Then |
|---|---|
| The best save is a discount past your Policy | It 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 conversation | It is handed to a person with the context, never offered a deal over an open issue. |
| A saved account disputes the renewal charge | The 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
| Term | This Service |
|---|---|
| Done | An account your risk rule flagged before its renewal date is still paying 30 days after that date. |
| Counted by | Renewal.saved |
| Billed | $120 a saved renewal, on acceptance |
| Target | Every flagged account renewing in the month. |
| Accepted when | The renewal payment clearing with no refund in 30 days accepts it. |
| Credited | An account that refunds or disputes the renewal payment inside 30 days. |
| Remedy | A flagged account that cancels is never billed, and its reason is written to the customer for the next rule. |
| Evaluator | Code reads the flag time from Silence watch and the renewal invoice and any refund from Stripe. |
| Window | 30 days before each renewal date to 30 days after it. |
| Assurance | measured |
| Oversight | none: 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.
| Count | What happened |
|---|---|
| 9 | Ordered |
| 9 | Flagged by the risk rule and sent a save note: 4 on usage, 3 on logins, 2 on a failed payment. |
| 6 | Still paying 30 days past renewal: 3 on a pause, 2 on a downgrade, 1 after the fix. |
| 3 | Cancelled anyway: never billed, each reason written to the customer. |
| 1 | Disputed the renewal charge inside 30 days: credited. |
| 5 | Accepted and billed: 5 × $120 = $600 |
Change it
| Ask | Compiles 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 |
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: and base(), or compose your own from these steps.
Works with
Plays that order it: Annual prepay, Next order due.
| Orders | Kind | Runs, each at its own price | Rolls up |
|---|---|---|---|
| Silence watch | Workflow | 30 × Free | Free |
| Save and win back | Agent task | 10 × About 0.7¢ a customer | About 7¢ |
It serves Keep and grow customers. It deepens Retention.
For agents
For agents: the guide, its approvals, retries and lookups
| Fact | Value |
|---|---|
| Guide | Renewal.save |
| Guide approval | None. |
| Goal | Save 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. |
| Retry | Three times with backoff, then the next route; the same idempotency key never charges, sends or posts twice |
| Idempotency key | leaseline-renewal-save-1 |
| Lookups | A 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
| Step | Function | Doer | Checker | Assurance | Inputs |
|---|---|---|---|---|---|
| 1. List the renewals due in 30 days | code | Code | Code reads each renewal date from the subscription in Stripe | measured | $0.horizon |
| 2. Score each one by the risk rule, daily, by Silence watch | code | Code | Code scores usage by the declared rule and reads each flag back | measured | $1.subscriptions, |
| 3. Send each flagged account one save note, by Save and win back | agentic | The CSM agent | A second model checks each note against the account’s record and your Policy | graded | $2.atRisk, |
| 4. Open the renewal on the saved terms | code | Code | Code reads the subscription back from Stripe with the new terms | measured | $3.accepted |
| 5. Read the payment 30 days past the renewal | code | Code | Code reads the renewal invoice and any refund from Stripe at day 30 | measured | $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
| Kind | What it touches |
|---|---|
| Systems | None. |
| Nouns | Collection, Customer, Renewal |
| Events | Collection.queried, Renewal.opened |
Reference
Inputs, outputs, its Event and its errors
| Name | Type | Default | Meaning |
|---|---|---|---|
rule | Policy | your retention Policy | The declared risk rule. The default: no login in 14 days, usage down 40% over 4 weeks, or a failed payment. |
horizon | string | '30d' | How far ahead it reads renewal dates. |
policy | Policy | your retention Policy | What 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).