customer.subscription.updated: what it means and what to email
Stripe sends customer.subscription.updated whenever a subscription changes. How to read previous_attributes, and which changes deserve an email.
- Sent
- Whenever a subscription changes: a renewal, a new plan, a coupon, a status change
- Stripe emails
- Not for the change itself, though renewal reminders and receipts go out on their own
- Kicklace
- A renewal or plan change, and a card going past due; other changes are ignored
What it means
Something about a subscription changed. It is the busiest of the subscription events, because Stripe sends it for almost everything: a renewal, a switch to another plan, a seat added, a coupon or discount applied, an invoice item added, the status moving from trial to active, and a customer pressing cancel on a plan that will run to the end of its period.
By itself the event does not say which of those it was. The answer is in previous_attributes.
When Stripe sends it
Each time any of those happens, so a single subscription sends it many times over its life, at least once every billing period.
The Event's data.previous_attributes holds only the fields that changed, with their old values, and when an array changed it holds the whole array. Read it first, before the subscription itself.
- A status change shows
status, for exampletrialingtoactiveoractivetopast_due. - A renewal shows the item's billing period. Since API version 2025-03-31.basil that period lives on each item, in
items.data[], and before that on the subscription ascurrent_period_end. - A new plan or seat count shows the old items in
previous_attributes.items.data[], price and quantity included. - A cancellation set for the end of the period shows
cancel_at_period_endgoing from false to true, and the subscription ends later withcustomer.subscription.deleted.
Stripe does not promise the order events arrive in, and the same one can arrive twice, so keep the ids of the ones you have handled.
Does Stripe email the customer?
Not for the change itself. Stripe has its own emails around a subscription's life: a reminder before a renewal if you turn on Send emails about upcoming renewals, which it sends for subscriptions that charge automatically, a receipt when the renewal is paid, and the failed payment email when it is not. None of them is sent because of this event.
The email to send
Only for the changes the customer would want a line about, and you pick them out of previous_attributes. An upgrade is the one worth writing, because they have just agreed to pay you more and that deserves a thank-you from you. A plan that will end at the period's close is worth a note too, with the date and the way back.
Subject: You're on [new plan] now
Hi {{first_name}},
Your [your app] plan is now [new plan], as you asked. [Say what is different, and when the new price starts.]
Thank you for moving up, and if anything about it isn't what you expected, reply to this email and I will sort it out.
[Your name]
Fields worth reading
previous_attributes: what changed, with the old values. Everything below is read against it.status:trialing,active,past_due,canceled,unpaid,paused,incompleteorincomplete_expired.items.data[].priceanditems.data[].quantity: the plan and the seats now.price.unit_amountandprice.recurring.intervalsay what it costs and how often.items.data[].current_period_end: when the current period ends.cancel_at_period_endandcancel_at: whether it ends at the close of the period, or on a date you set.cancellation_details: why it is ending, when it is.default_payment_methodandlatest_invoice: the card, and the invoice that last came out of it.
if (event.type === "customer.subscription.updated") {
const subscription = event.data.object;
const before = event.data.previous_attributes;
const nowPastDue = before?.status !== undefined && subscription.status === "past_due";
const cancelling = before?.cancel_at_period_end === false && subscription.cancel_at_period_end;
}
To see a real payload, run stripe trigger customer.subscription.updated in a sandbox and read the event it sends.
What Kicklace does
With Stripe connected, three things come out of it, and the rest are ignored.
- A status that just turned past due lands on the person as a failed payment,
payment_failed, with the subscription brought up to date. It is the same failureinvoice.payment_failedreports, so the two land as one. - A renewal, or a change to the items, the seats or the price, lands as a renewal,
subscription_renewed, and the subscription on the account is brought up to date. A plan or seat count that went up also moves the account to Expanding, and the account's row says what went up. - Every other change is ignored, a cancellation set for the end of the period included. Kicklace hears that one when
customer.subscription.deletedarrives, at the end.
A side road can start from subscription_renewed once one has arrived, and the Expanding stage can carry a sequence of its own for an upgrade. Nothing sends until you switch it on, and Kicklace shows who would have got it over the last 30 days first.
Write yours from your URL. Paste your site's address into the welcome email generator and it writes your first emails in your site's voice, ready to copy, with no sign-up. The upgrade note is one you add on the map, where Write for me drafts it in your voice.
Sources
Checked against Stripe’s docs on .
Free up to 1,000 contacts.
Join the journey
We're building Kicklace so you can get back to building your own product. We'll email you when there's something new worth showing.