Churn prevention emails: what to send before and after a customer cancels

Churn prevention emails go out at three moments: when usage drops, when a customer cancels, and after they leave. This guide shows the first email for each and the Loops workflow that sends it, by hand or with an AI agent.

What churn prevention emails are

Churn prevention emails go to customers who are drifting away, canceling, or already gone. Each one starts from a change in your app’s data, so it arrives when it matters and stops when the customer comes back.

This guide covers customers who decide to leave. When a card fails, dunning emails handle it. Unsubscribes from your list are a different measure, covered in email churn rate.

Where the signals come from

Loops can’t see inside your product. Your app decides when a customer has gone quiet or asked to cancel, then tells Loops with a contact property or an event.

Set it up with an AI agent

Connect Claude, ChatGPT, or Codex through the Loops MCP server, or give Claude Code or Cursor the Loops CLI and agent skills. Then paste a prompt:

Send the signalsIn our app, update the Loops contact with churnRisk every day (true when their last project was 14 to 60 days ago), send a cancellationRequested event with the cancel reason, and set subscriptionStatus to Canceled when a subscription ends and Active when one starts. Create the contact properties first.
Build the workflowsCreate three draft workflows in Loops: one that starts when churnRisk changes to true, one on cancellationRequested that branches on cancelReason, and a win-back that waits 30 days after subscriptionStatus changes to Canceled and tests two first emails in an experiment.
Write the emailsWrite the first email for each of these workflows in LMX, with a fallback for First Name, then send me previews.

The agent adds the Loops SDK calls to your app, creates the contact properties, and builds each workflow as a draft with the Workflows API, including triggers, timers, filters, branches, and experiments. Then it writes each email in LMX and sends you a preview.

An agent can’t start a workflow, so you review each draft and start it in Loops. The workflow endpoints are in alpha. See Loops for AI agents.

Three moments, three emails

Each moment gets its own workflow. These are the first emails in each, from a made-up product called Acme.

1
Usage dropsHelp them back in

Your app sets churnRisk to true. A Contact updated trigger starts the workflow.

Set it up
A
Your projects are where you left themAcme
Acme

Pick up where you left off

Hi First Name, it’s been a couple of weeks since you opened Acme. Your projects are saved, and there’s a faster way to start the next one.

NewProject templates
Open my projects
Not using Acme anymore? You can cancel in Settings.
AcmeQuestions? Just reply to this email.
2
They cancelOne offer for their reason

Your cancel flow sends cancellationRequested with the reason. A Branch picks the email.

Set it up
A
Pause your plan instead?Acme
Acme

Want to pause instead?

Hi First Name, thanks for telling us why you’re leaving. If the price is the problem, you can pause your plan for up to 3 months and keep your projects as they are.

Your reasonCancel Reason
While pausedNo charges
Pause my plan
Still want to cancel? You don’t need to do anything.
AcmeQuestions? Just reply to this email.
3
30 days after it endsShow what changed

subscriptionStatus changes to Canceled, then a 30-day timer. An Experiment tests this email against a discount.

Set it up
A
What’s new in Acme since you leftAcme
Acme

A few things changed

Hi First Name, here’s what we’ve shipped since you left. Your old projects are still in your account.

  1. 1Project templates
  2. 2Shared views for clients
  3. 3Exports in half the time
Restart my plan
AcmeQuestions? Just reply to this email.
Each email is the first in its own workflow. First Name and Cancel Reason are contact properties your app keeps up to date.

Give every merge tag a fallback value. If a contact has no value for a property and there’s no fallback, Loops doesn’t send the email.

When usage drops

Pick the action that shows a customer is getting value, such as creating a project. A login doesn’t count. Your app checks it every day and sets a churnRisk contact property to true when the last one was 14 to 60 days ago. The Loops churn risk recipe stops at 60 days to protect your sender reputation.

Contact updated
Churn Risk changed
your app flips it to true
Audience filter
by Churn Risk
still at risk? they go on
Send email
Pick up where you left off
Timer
Wait 4 days
back in the app? they leave here
Send email
Is Acme still right for you?
The filter applies to all following nodes, so a customer who comes back during the wait never gets the second email.

Use the Contact updated trigger with churnRisk changing to true, and set it to Every time so a customer who drifts again later starts over. Set the audience filter to All following nodes. When your app sets churnRisk back to false, the customer leaves before the next email.

Two or three emails a few days apart is enough. Lead with help: the customer hasn’t decided to leave, so a discount can wait.

When a customer cancels

Ask for the reason in your cancel flow, before the customer confirms. Then your app sends a cancellationRequested event and sets cancelReason and subscriptionStatus on the contact in the same call.

Event received
cancellationRequested
sent from your cancel flow
Branch
2 branches
Audience filter
by Cancel Reason
too expensive
Send email
Pause your plan instead?
Audience filter
All contacts
everyone else
Send email
Anything we could fix?
Filters are checked from left to right. The last one has no conditions, so everyone else lands there.

A Branch sends each customer down the first path whose filter they match. Make one offer that fits the reason: a pause or a smaller plan when price is the problem, a reply from the team when a feature is missing.

Send the cancellation confirmation itself as transactional email from your server. Workflow emails only reach subscribed contacts, and every customer needs the confirmation.

After they leave: win-back

When the subscription ends, your app sets subscriptionStatus to Canceled, and back to Active if they restart. A Contact updated trigger on the change to Canceled starts the win-back workflow, and a 30-day timer gives the customer some room.

Contact updated
Subscription Status changed
Timer
Wait 30 days
give them some room
Audience filter
by Subscription Status
restarted? they leave here
Experiment
50% → 1 variant50% → control
half get each email
Variant
50.00% distribution
Send email
20% off if you come back
Control
50.00% distribution
Send email
What’s new since you left
The experiment reports sends, opens, and clicks for each email. Count restarts in your own data.

An Experiment splits customers between two first emails. Here half hear what changed and half get a discount, so you learn whether the offer is worth giving away. For more copy, see these win-back email examples.

No app code yet? The Loops Stripe app can start this workflow from Stripe’s customer.subscription.deleted event. Stripe events carry only the customer’s email and name, so the filter that catches restarts still needs a property from your app.

What to put in each email

A
Your projects are where you left themAcme
Acme

Pick up where you left off

Hi First Name, it’s been a couple of weeks since you opened Acme. Your projects are saved, and there’s a faster way to start the next one.

says why you’re writing
NewProject templates
one reason to come back
Open my projectsone button back in
Not using Acme anymore? You can cancel in Settings.
an honest way out
AcmeQuestions? Just reply to this email.
a person reads replies
No discount yet. The customer hasn’t decided to leave.

Match the email to the moment. A quiet customer needs one reason to come back. A canceling customer needs one offer that fits what they told you. A former customer needs to hear what changed. Each gets one button.

End the usage drop sequence with a direct question and a link to cancel, as the Loops recipe suggests. Customers who want to leave can, and the rest know someone asked.

Send the signals from your app

Three calls cover all three moments. Create churnRisk as a boolean and cancelReason and subscriptionStatus as strings in Loops first.

import { LoopsClient } from "loops";

const loops = new LoopsClient(process.env.LOOPS_API_KEY);

// Daily job: at risk when the last project was 14 to 60 days ago.
export async function syncChurnRisk(user) {
  const days = (Date.now() - user.lastProjectAt) / 86_400_000;
  await loops.updateContact({
    email: user.email,
    properties: { churnRisk: days >= 14 && days <= 60 },
  });
}

// Cancel flow: after the customer picks a reason and confirms.
export async function cancellationRequested(user, reason) {
  await loops.sendEvent({
    email: user.email,
    eventName: "cancellationRequested",
    contactProperties: { cancelReason: reason, subscriptionStatus: "Canceling" },
  });
}

// Billing webhook: "Canceled" when a subscription ends, "Active" when one starts.
export async function setSubscriptionStatus(user, status) {
  await loops.updateContact({
    email: user.email,
    properties: { subscriptionStatus: status },
  });
}

Run the first function from a daily job. The Contact updated trigger fires when a value changes, so sending the same value again starts nothing.

Measure returns, not opens

Decide what counts as a return before you send: an at-risk customer who creates a project within 14 days, a canceling customer who stays, or a former customer who restarts. Count those in your own data.

Experiments report sends, opens, and clicks for each email. Use them to choose a subject line or an offer, then check returns in your product. For the metrics behind this, see email marketing KPIs.

Common questions

What is churn prevention?

Churn prevention is the work of keeping customers who are likely to cancel. In email, it means messages triggered by product signals: a drop in usage, a cancellation request, or a subscription that ended. Each one gives the customer a reason to stay or come back.

What should a churn prevention email say?

One reason to come back and one button. A customer whose usage dropped needs help with the work they started. A customer who is canceling needs one offer that fits their reason, such as a pause. A former customer needs to hear what changed since they left.

When should I send a win-back email?

Give the customer a few weeks after the subscription ends. The example in this guide waits 30 days, then sends one email from an experiment. Stop as soon as they restart.

Can Loops tell when a customer’s usage drops?

No. Loops doesn’t see activity inside your product. Your app decides when usage has dropped and updates a contact property, such as churnRisk, or sends an event. A Loops workflow starts from that change.

Should a cancellation email be transactional?

The confirmation that a plan is canceled should be, because every customer needs it. In Loops, transactional email also reaches contacts who unsubscribed. A save offer or a question about why they left is a workflow email, which only reaches subscribed contacts.

Can an AI agent set up churn prevention emails in Loops?

Yes. Through the Loops MCP server or the CLI, an agent can add the tracking calls to your app, create the contact properties, build each workflow as a draft with its triggers, branches, and experiments, and write the emails in LMX. You review the drafts and start them in Loops.

What's next

More on product and lifecycle email.

See all guides