Skip to content
Groundmail

Guides

How to customize Stripe receipt emails (or send your own)

What you can change in Stripe's receipt emails, where the settings are, and how to send your own receipt on invoice.paid with the amount and PDF link.

By GroundmailUpdated All guides →

Short answer

Stripe sends a receipt email after each successful payment or refund when you turn on Successful payments or Refunds under Settings → Business → Customer emails. You can change the logo, colors, business details and language. You can't change the layout or the wording, and nothing is sent when a payment fails.

To send a receipt in your own template, listen for the invoice.paid event (subscriptions and invoiced payments) and send an email with the amount, the invoice number and the links Stripe gives you to the hosted invoice and its PDF. Then turn off Stripe's Successful payments email so customers don't get two receipts.

How Stripe receipt emails work

  • When: after a successful payment, including invoice and subscription payments, and after a refund. For payment methods that confirm later, such as bank debits, the receipt waits until the payment succeeds.
  • Turn on: Settings → Business → Customer emails, then Successful payments and Refunds.
  • Test payments: Stripe's docs say to send a receipt for a test payment by hand, from the payment's Receipt history.
  • Resend: open the payment, go to Receipt history, choose Send receipt and confirm the address.
  • Link: every receipt has a web version at the charge's receipt_url. The link expires after 30 days; after that Stripe asks for the customer's email and sends a fresh one.
  • From: stripe.com, unless you verify your own email domain in Stripe, which sends receipts from receipts@ on your domain.

For one-time payments through Checkout you can also have Stripe create a paid invoice (invoice_creation[enabled]=true), so the customer gets an invoice summary with PDF links to the invoice and the receipt. Stripe prices post-payment invoices for Checkout separately; see its receipts docs.

What you can customize

SettingWhereWhat it changes
Logo or iconSettings → Business → Branding → Email receiptsSquare PNG or JPG, at least 128 × 128 px, up to 512 KB
Brand and accent colorSame pageThe colors of the email and Stripe's hosted pages
Business detailsSettings → Business → Business details → Public detailsName, support email, support address and privacy policy link. Stripe requires these on receipts.
LanguageCustomer's preferred locale, or Default language in Customer emailsThe language of the receipt
Sending domainCustomer emails → custom email domainSends from your domain instead of stripe.com

From Stripe's receipts and branding docs.

Last checked on each vendor's public page.

That's the list. There's no field for your own subject line, intro text, product names in your words, a link back into your app, or a note like "your plan renews on the 5th". If you need any of those, send the receipt yourself.

Send your own receipt on invoice.paid

Stripe fires invoice.paid when an invoice is paid, which covers every subscription renewal and any invoice you charge. The invoice in the event has what a receipt needs:

Receipt lineInvoice field
Amount paidamount_paid (in the smallest currency unit) and currency
Invoice numbernumber
View online, update cardhosted_invoice_url
PDFinvoice_pdf
Who to send it tocustomer_email, customer_name
What they boughtlines.data[0].description
Node (Express): send a receipt on invoice.paid
import express from "express";
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const app = express();

// Stripe signs the raw body, so don't let a JSON parser touch this route.
app.post("/stripe/webhook", express.raw({ type: "application/json" }), async (req, res) => {
  let event;
  try {
    event = stripe.webhooks.constructEvent(
      req.body,
      req.headers["stripe-signature"],
      process.env.STRIPE_WEBHOOK_SECRET,
    );
  } catch (err) {
    return res.status(400).send(`Webhook signature check failed: ${err.message}`);
  }

  if (event.type === "invoice.paid") {
    const invoice = event.data.object;
    // Skip $0 invoices, such as the one Stripe creates when a free trial starts.
    if (invoice.amount_paid > 0 && invoice.customer_email) {
      try {
        await sendReceipt(event.id, invoice);
      } catch (err) {
        return res.status(500).send("Send failed; Stripe will retry"); // safe: the send is idempotent
      }
    }
  }
  res.sendStatus(200);
});

async function sendReceipt(eventId, invoice) {
  const amount = new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: invoice.currency.toUpperCase(),
  }).format(invoice.amount_paid / 100); // divide by 1 for zero-decimal currencies like JPY
  const item = invoice.lines.data[0]?.description ?? "your subscription";

  const res = await fetch("https://api.groundmail.dev/emails", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.GROUNDMAIL_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": `stripe:${eventId}`, // a retried event can't send twice
    },
    body: JSON.stringify({
      from: "Example Billing <billing@mail.example.com>",
      to: invoice.customer_email,
      subject: `Your receipt for ${amount} (invoice ${invoice.number})`,
      text: `We received ${amount} for ${item}.\n\nInvoice ${invoice.number}: ${invoice.hosted_invoice_url}\nPDF: ${invoice.invoice_pdf}`,
      html: `<p>We received <strong>${amount}</strong> for ${escapeHtml(item)}.</p>
<p>Invoice ${invoice.number}</p>
<p><a href="${invoice.hosted_invoice_url}">View invoice</a> · <a href="${invoice.invoice_pdf}">Download PDF</a></p>`,
      tags: [{ name: "type", value: "receipt" }],
    }),
  });
  if (!res.ok) throw new Error(`Groundmail ${res.status}: ${await res.text()}`);
}

escapeHtml stands in for your own helper: line item descriptions can contain characters that aren't safe in HTML. In Stripe, create the endpoint under Webhooks, subscribe it to invoice.paid, choose the snapshot payload so the event carries the whole invoice, and copy its whsec_ signing secret. For local testing, stripe listen --forward-to localhost:3000/stripe/webhook prints a secret and forwards test events.

The Idempotency-Key makes a resent event return the original send instead of a second email. Groundmail keeps keys for 24 hours and Stripe retries for up to three days, so also record the event ids you've handled; Send emails from Stripe webhooks shows how.

Or use the Receipt starter

With the Stripe integration connected, Automations → New → From Stripe → Receipt creates an automation that starts on stripe.invoice.paid and sends one email with the amount and a link to the invoice. Edit it like any template, with these fields:

FieldExample
{{event.data.customer.name}}Maria
{{event.data.invoice.amount}}$29.00
{{event.data.invoice.number}}A1F2-0042
{{event.data.invoice.url}}Stripe's hosted invoice page
{{event.data.invoice.pdf}}Invoice PDF link
{{event.data.plan.name}}The first line item on the invoice

It opens as a draft and sends from your verified domain on the transactional stream once you turn it on. The free receipt email template is a starting point for the design.

Turn off Stripe's receipt so customers don't get two

  1. 01Check your own receipt arrivesPay a test invoice and confirm your email went out with the right amount and links.
  2. 02Open Customer emails in StripeSettings → Business → Customer emails.
  3. 03Turn off Successful paymentsLeave Refunds on unless you also send your own refund emails.

Turning it off doesn't change the receipt itself: the charge still has a receipt_url, and you can still send Stripe's receipt by hand from the payment page when someone asks for it.

What a good receipt includes

  • The amount and currency, and what it was for, in the words your customer uses.
  • The date and the invoice or receipt number, so they can match it to a bank statement.
  • A link to the invoice and the PDF, for their accountant.
  • When the next payment is due, for subscriptions.
  • Your business name and a way to reach support. If you replace Stripe's receipt, keep the details Stripe requires on its own: legal business name, support address, support email and privacy policy link.

Frequently asked questions

How do I turn on Stripe receipt emails?

In the Stripe dashboard open Settings → Business → Customer emails and turn on Successful payments (and Refunds if you want refund receipts).

Why didn't my test payment send a receipt?

Stripe's docs say to send receipts for test payments by hand: open the payment, go to Receipt history and choose Send receipt.

Can I change the text of a Stripe receipt?

No. You can change the logo, colors, business details and language. For your own subject and wording, send the receipt yourself on invoice.paid and turn off Stripe's Successful payments email.

Does Stripe send a receipt when a payment fails?

No. Receipts are only sent for successful payments. Failed subscription payments have their own email; see Stripe failed payment emails.

Which event should I send a receipt on?

invoice.paid for subscriptions and invoices. For one-time payments without an invoice, checkout.session.completed or payment_intent.succeeded.

Sources

Facts on this page about other companies' products, prices and policies come from these public pages. They change, so check them before you decide.

Email for apps. Set up by your agent.

Sign-in codes, receipts and alerts kept separate from marketing mail, with inbound, templates and automations included. Start free with 3,000 emails a month, and move to a dedicated IP or server without changing your code.