Win back canceled customers with an offer for why they left
Reads why each Stripe subscription was canceled, sorts the reason with Jev, and emails a win-back offer that fits it with Resend.
Outcome
A table of every recent cancellation with its reason, whether it leaves the door open, the offer picked and Jev's confidence.
An approved win-back email sent to each customer who got an offer and opted in to marketing email.
The customers left out, with why.
How it works
- 1
Pull cancellations
StripeList subscriptions with status
canceled, each customer expanded, and page through them. Keep those whoseended_atfalls in the lastlookback_daysand whosecancellation_details.reasoniscancellation_requested, with each customer's name and email, the plan, how long they subscribed, and the cancellation feedback and comment; list the rest as left out. - 2
Sort the reasons
TypeSafe AISend each cancellation's feedback, comment, plan and tenure as the state, with a choice
reasonof too_expensive (price or budget), missing_feature (needs something the product lacks), low_usage (did not use it enough), switched_to_competitor (moved to another product), too_complex (hard to set up or use), support_problem (unhappy with help received), temporary_need (a project or season ended), business_closed (the company closed or was acquired) and other, and a nouldoor_open(the comment names something that would bring them back). Keep each reason, its confidence anddoor_open. - 3
Pick the offers
Your agentShow the user every cancellation whose reason confidence is below
min_confidenceor whosedoor_openis unsure, and keep what the user decides. Give each customer the offer inoffersfor their reason. Leave out business_closed, switched_to_competitor unlessdoor_openis yes, anyone with no offer for their reason and anyoneconsent_sourcedoesn't show as opted in, and list each with why. Confirm the consent list with the user. - 4
Write the emails
Your agentFor each customer, draft a subject line and three or four plain sentences: say what they told you in their own words, make the one offer for their reason, link to
comeback_link, and end with a line to opt out of these emails andpostal_address. Show the drafts to the user. - 5
Send
ResendAfter the user approves, send each email from
from_addressto its customer, with an idempotency key per customer.
You'll be asked for
How recent a cancellation counts
e.g. 14
The offer for each reason, with any promotion code already created in Stripe
e.g. too_expensive: 30% off for three months with the code COMEBACK30; low_usage: a free setup call; too_complex: a free setup call; missing_feature: a note when it ships; temporary_need: a free month when they return
The page where a customer restarts their subscription
e.g. https://app.acme.com/billing
Where you record who opted in to marketing email
e.g. a Stripe customer metadata field, or an export of opted-in addresses from your email tool
The verified Resend sender the emails come from
e.g. Sam at Acme <sam@acme.com>
The mailing address every marketing email carries
e.g. Acme Inc., 1 Main St, Springfield, IL 62701
How sure Jev must be before its answer is used without you
e.g. 0.8; a yes-or-no answer counts as yes at or above it and as no at or below 1 minus it
The file your agent runs
cancellation-winback.md
Win back canceled customers with an offer for why they left
Reads why each Stripe subscription was canceled, sorts the reason with Jev, and emails a win-back offer that fits it with Resend.
Set up the tools below, then run the steps in order for the user, carrying each step's results into the next. The run is done when the user has the outcome below.
Outcome
- A table of every recent cancellation with its reason, whether it leaves the door open, the offer picked and Jev's confidence.
- An approved win-back email sent to each customer who got an offer and opted in to marketing email.
- The customers left out, with why.
Inputs
Ask the user for these before you start.
lookback_days: how recent a cancellation counts, e.g. 14offers: the offer for each reason, with any promotion code already created in Stripe, e.g. too_expensive: 30% off for three months with the code COMEBACK30; low_usage: a free setup call; too_complex: a free setup call; missing_feature: a note when it ships; temporary_need: a free month when they returncomeback_link: the page where a customer restarts their subscription, e.g. https://app.acme.com/billingconsent_source: where you record who opted in to marketing email, e.g. a Stripe customer metadata field, or an export of opted-in addresses from your email toolfrom_address: the verified Resend sender the emails come from, e.g. Sam at Acme sam@acme.compostal_address: the mailing address every marketing email carries, e.g. Acme Inc., 1 Main St, Springfield, IL 62701min_confidence: how sure Jev must be before its answer is used without you, e.g. 0.8; a yes-or-no answer counts as yes at or above it and as no at or below 1 minus it
Set up
List subscriptions (Stripe, tool:stripe/list-subscriptions)
Use the first option your agent supports.
MCP (official, remote)
Add this server to your agent's MCP settings, then sign in when asked.
{ "mcpServers": { "stripe": { "url": "https://mcp.stripe.com" } } }
Call the MCP tool stripe_api_read with GET /v1/subscriptions.
CLI (official)
Install the command and sign in with it, then confirm it runs.
npm install -g @stripe/cli
stripe --version
Run stripe subscriptions list.
Answer typed questions (TypeSafe AI, tool:typesafe/answer-typed-questions)
Use the API.
- Base URL: https://api.typesafe.ai
- Endpoint:
POST /v1/systemone - Auth: send the header
Authorization: Bearer $TYPESAFE_API_KEY - Get a key: https://console.typesafe.ai
Note: Put every question about one record in one request. GET /v1/models lists the models and is the cheapest check of a key; a request over the rate limit gets a 429 with a Retry-After header.
Note: Send state, model (jev-latest) and named questions, each with type, instructions and criteria: a choice maps labels to descriptions, a score lists its levels in order, a noul's is optional. Read a score by its most likely level; send unsure answers to a person.
Send an email (Resend, tool:resend/send-email)
Use the first option your agent supports.
Note: Pass an Idempotency-Key header (--idempotency-key on the CLI) so a retry never sends it twice; keys expire after 24 hours.
MCP (official, remote)
Add this server to your agent's MCP settings, then sign in when asked.
{ "mcpServers": { "resend": { "url": "https://mcp.resend.com/mcp" } } }
Call the MCP tool send-email.
CLI (official)
Install the command, then confirm it runs.
npm install -g resend-cli
resend --version
Run resend emails send.
Set $RESEND_API_KEY in your environment first (get a key: https://resend.com/api-keys).
Before step 1, confirm access to each service with its cheapest read-only call, like a list or a search. Never send or change anything to test access.
Steps
- Pull cancellations with List subscriptions (Stripe). List subscriptions with status
canceled, each customer expanded, and page through them. Keep those whoseended_atfalls in the lastlookback_daysand whosecancellation_details.reasoniscancellation_requested, with each customer's name and email, the plan, how long they subscribed, and the cancellation feedback and comment; list the rest as left out. - Sort the reasons with Answer typed questions (TypeSafe AI). Send each cancellation's feedback, comment, plan and tenure as the state, with a choice
reasonof too_expensive (price or budget), missing_feature (needs something the product lacks), low_usage (did not use it enough), switched_to_competitor (moved to another product), too_complex (hard to set up or use), support_problem (unhappy with help received), temporary_need (a project or season ended), business_closed (the company closed or was acquired) and other, and a nouldoor_open(the comment names something that would bring them back). Keep each reason, its confidence anddoor_open. - Pick the offers yourself. Show the user every cancellation whose reason confidence is below
min_confidenceor whosedoor_openis unsure, and keep what the user decides. Give each customer the offer inoffersfor their reason. Leave out business_closed, switched_to_competitor unlessdoor_openis yes, anyone with no offer for their reason and anyoneconsent_sourcedoesn't show as opted in, and list each with why. Confirm the consent list with the user. - Write the emails yourself. For each customer, draft a subject line and three or four plain sentences: say what they told you in their own words, make the one offer for their reason, link to
comeback_link, and end with a line to opt out of these emails andpostal_address. Show the drafts to the user. - Send with Send an email (Resend). After the user approves, send each email from
from_addressto its customer, with an idempotency key per customer.
Notes
Each offer answers one reason: a discount for price, a setup call for low usage or complexity. A customer who switched to a competitor is usually better left alone. Stripe's own feedback values are broad; the free-text comment is where the detail is, which is why Jev reads both. Customers whose subscriptions ended over a failed payment or a dispute need a billing fix, not an offer, so step 1 leaves them out.
Run it weekly with lookback_days set to 7.
Rules
- Use only the services set up above. The read-only calls they need, like listing ids or polling for results, are fine.
- Ask the user before anything that sends messages, costs money, or changes data, and say how many records it touches. One approval covers a batch the user has seen.
- Never print API keys.
