<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kicklace</title>
    <description>The latest articles on DEV Community by Kicklace (kicklace).</description>
    <link>https://dev.to/kicklace</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F15568%2Ff4bc6c7b-fe35-4bb1-b83c-094def480580.png</url>
      <title>DEV Community: Kicklace</title>
      <link>https://dev.to/kicklace</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kicklace"/>
    <language>en</language>
    <item>
      <title>customer.subscription.deleted: email a customer when Stripe ends it</title>
      <dc:creator>Michael Fall</dc:creator>
      <pubDate>Sat, 07 Nov 2026 00:00:12 +0000</pubDate>
      <link>https://dev.to/kicklace/customersubscriptiondeleted-email-a-customer-when-stripe-ends-it-3dan</link>
      <guid>https://dev.to/kicklace/customersubscriptiondeleted-email-a-customer-when-stripe-ends-it-3dan</guid>
      <description>&lt;p&gt;You have a paying customer whose subscription has just ended, and Stripe has sent one event and nothing else. The event doesn't say whether they chose to leave, and that matters, because someone whose card kept failing never decided anything, and a "sorry to see you go" note is the wrong thing to send them. The few who did choose to leave are worth one short email asking why, and their reply is where you learn something. The guide below shows when Stripe sends the event, how to read its reason, and the email I would send.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;When a subscription ends, Stripe sends an event called &lt;code&gt;customer.subscription.deleted&lt;/code&gt;. It is the moment a paying customer stops being one, and the best thing to send is short: say it has ended, ask why in one line, and leave the door open.&lt;/p&gt;

&lt;p&gt;The event on its own, with the fields worth reading, is on the &lt;a href="https://www.kicklace.com/stripe/customer-subscription-deleted?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=post_sxrgym9smdyj" rel="noopener noreferrer"&gt;customer.subscription.deleted&lt;/a&gt; page.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Stripe sends it
&lt;/h2&gt;

&lt;p&gt;It depends on how the subscription was cancelled.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;At the end of the period.&lt;/strong&gt; Most cancel buttons, Stripe's customer portal included, cancel at the end of the period the customer already paid for. Stripe sends &lt;code&gt;customer.subscription.updated&lt;/code&gt; with &lt;code&gt;cancel_at_period_end&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt; when they press it, and &lt;code&gt;customer.subscription.deleted&lt;/code&gt; when the period runs out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Straight away.&lt;/strong&gt; A subscription cancelled at once sends &lt;code&gt;customer.subscription.deleted&lt;/code&gt; right away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After failed payments.&lt;/strong&gt; If payments keep failing and your Stripe settings cancel the subscription when retries run out, it ends the same way, with &lt;code&gt;cancellation_details.reason&lt;/code&gt; set to &lt;code&gt;payment_failed&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one matters. Somebody whose card expired never decided to leave, and "sorry to see you go" is the wrong email for them. Check the reason before you send anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your own webhook
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add a webhook endpoint in Stripe that listens for &lt;code&gt;customer.subscription.deleted&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Check the signature on every request. Stripe signs each one in a &lt;code&gt;Stripe-Signature&lt;/code&gt; header and needs the raw body untouched.&lt;/li&gt;
&lt;li&gt;Return a 2xx fast and send the email after. Keep the event ids you have handled, because an event can arrive twice.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webhooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constructEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;endpointSecret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;customer.subscription.deleted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cancellation_details&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// "cancellation_requested", "payment_failed", ...&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="kd"&gt;as &lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// customer.email, customer.name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The subscription carries the customer's id rather than their email, so fetch the customer. If your portal asks why they're leaving, &lt;code&gt;cancellation_details.feedback&lt;/code&gt; and &lt;code&gt;cancellation_details.comment&lt;/code&gt; hold the answer, and your email shouldn't ask again.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the email should say
&lt;/h2&gt;

&lt;p&gt;Keep it to a few lines, from you, with nothing to sell.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Subject: Your [your app] plan has ended&lt;/p&gt;

&lt;p&gt;Hi {{first_name}},&lt;/p&gt;

&lt;p&gt;Your [your app] plan has ended, and you won't be charged again. Your account and everything in it is still here if you come back.&lt;/p&gt;

&lt;p&gt;Could you tell me why you left? One line is plenty, and you can reply to this email.&lt;/p&gt;

&lt;p&gt;[Your name]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The reply is the useful part. A &lt;a href="https://www.kicklace.com/cancellation-survey-email-template?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=post_sxrgym9smdyj" rel="noopener noreferrer"&gt;cancellation survey email template&lt;/a&gt; has more ways to ask, and &lt;a href="https://www.kicklace.com/win-back-emails?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=post_sxrgym9smdyj" rel="noopener noreferrer"&gt;win-back emails&lt;/a&gt; cover what to send weeks later.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is customer.subscription.deleted the same as a cancellation?
&lt;/h3&gt;

&lt;p&gt;It's the end of the subscription. A customer who cancels at the end of the period has cancelled before this event arrives, and is still paying until it does.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I email when they press cancel, or when it ends?
&lt;/h3&gt;

&lt;p&gt;When it ends is the safer default, because it's when the change is real. You can also send a short "got it" when they press cancel and keep the question about why for when it ends.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if a failed payment ended it?
&lt;/h3&gt;

&lt;p&gt;Send the &lt;a href="https://www.kicklace.com/stripe-failed-payment-email?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=post_sxrgym9smdyj" rel="noopener noreferrer"&gt;failed payment email&lt;/a&gt; instead, with a way to update the card. That customer may want to stay.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was written with the help of AI.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>saas</category>
      <category>email</category>
      <category>startup</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Changelog email: turn your Vercel deploys into a product update</title>
      <dc:creator>Michael Fall</dc:creator>
      <pubDate>Sat, 31 Oct 2026 01:00:10 +0000</pubDate>
      <link>https://dev.to/kicklace/changelog-email-turn-your-vercel-deploys-into-a-product-update-4c5b</link>
      <guid>https://dev.to/kicklace/changelog-email-turn-your-vercel-deploys-into-a-product-update-4c5b</guid>
      <description>&lt;p&gt;Every production deploy you ship is a small piece of news for the people using your app, and most of them never hear it. I'd gather a week of deploys, keep the changes a customer would notice, and send one short note from my own name instead of an email on every push. The lines for that note can come from the commit and pull request titles, so it helps to write those titles for a reader. The article below walks through the Vercel side, the plain way to do it by hand, and what the email should say.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;Do not email people on every deploy. Gather a week of them, pick what a customer would notice, and send one short update to the people who asked to hear from you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The plain way
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Once a week, open your Vercel deployments, or run &lt;code&gt;git log&lt;/code&gt;, and look at what went to production.&lt;/li&gt;
&lt;li&gt;Keep the changes a customer would notice. Drop the refactors and the fixes nobody saw.&lt;/li&gt;
&lt;li&gt;Write three or four lines in plain words.&lt;/li&gt;
&lt;li&gt;Send them to a list of people who agreed to get product updates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Vercel can call a webhook of yours when a deploy succeeds. Its events include &lt;code&gt;deployment.succeeded&lt;/code&gt;, and the payload says whether the target was production. As of October 2026, Vercel's account webhooks are on its Pro and Enterprise plans. A webhook is handy for collecting deploys in one place, but you still choose the words.&lt;/p&gt;

&lt;p&gt;The hard part is not the sending. It is remembering to do it, every week, while you are busy building.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the email should say
&lt;/h2&gt;

&lt;p&gt;Lead with what changed for the reader, in their words, and keep it short.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Subject: What's new in [your app]&lt;/p&gt;

&lt;p&gt;Hi {{first_name}},&lt;/p&gt;

&lt;p&gt;This week we shipped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[Date]: [what changed, in a customer's words]&lt;/li&gt;
&lt;li&gt;[Date]: [what changed, in a customer's words]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Something you wish we would build next? Reply to this email.&lt;/p&gt;

&lt;p&gt;[Your name]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;More in &lt;a href="https://www.kicklace.com/product-update-emails?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=post_nqsoukvcrj4" rel="noopener noreferrer"&gt;product update emails&lt;/a&gt; and the &lt;a href="https://www.kicklace.com/product-update-email-template?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=post_nqsoukvcrj4" rel="noopener noreferrer"&gt;product update email template&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does every deploy send an email?
&lt;/h3&gt;

&lt;p&gt;No. Only the list's scheduled email goes out, and a week with nothing deployed sends nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need GitHub?
&lt;/h3&gt;

&lt;p&gt;No. Without it the lines come from each deploy's title, the first line of its commit message. GitHub adds the pull request titles.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was written with the help of AI.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>saas</category>
      <category>email</category>
      <category>startup</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
