Stripe sends receipts — not your product emails
Stripe can send a receipt. It does not send your onboarding email, your dunning sequence or your plan-change confirmation.
Stripe integration
Receive Stripe webhook events in your backend, forward them to else.events and let rules match the right template. No billing email logic in your webhook handler.
Stripe can send a receipt. It does not send your onboarding email, your dunning sequence or your plan-change confirmation.
invoice.payment_failed, customer.subscription.updated, checkout.session.completed — each needs its own email path in your webhook handler.
Billing email copy is hardcoded in your webhook handler or in an inline template string.
First failure, second failure, final notice — that sequencing logic ends up in your codebase.
Receive the Stripe webhook, validate the signature, extract user and billing data, POST to else.events. One pattern for every Stripe event.
Set rules: invoice.payment_failed + plan=Pro → Pro dunning template. invoice.payment_failed + attempt=3 → final notice template. No if/else in your handler.
Change your payment failed copy or CTA in the else.events dashboard — no webhook handler change.
See which Stripe event triggered which rule, which template rendered and whether the email was delivered.
// Node.js Stripe webhook → else.events
// webhook handler (Express / Next.js route handler)
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
const sig = req.headers['stripe-signature'] as string;
const event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
if (event.type === 'invoice.payment_failed') {
const invoice = event.data.object as Stripe.Invoice;
const customer = await stripe.customers.retrieve(invoice.customer as string) as Stripe.Customer;
await fetch('https://app.else.events/api/events', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.ELSE_EVENTS_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'invoice.payment_failed',
user: { email: customer.email!, name: customer.name ?? 'Customer' },
data: { plan: invoice.metadata?.plan, amount: (invoice.amount_due / 100).toFixed(2),
currency: invoice.currency.toUpperCase(), attempt: invoice.attempt_count,
update_payment_url: invoice.hosted_invoice_url },
}),
});
}
res.json({ received: true });
}); Validate the Stripe signature, extract customer data, forward as an else.events event. Rules and templates handle what email gets sent — not your webhook handler.
invoice.payment_failed First, second and final payment failure emails with different copy — handled by rules, not code.
customer.subscription.updated Plan upgrade, downgrade or cancellation confirmation with dynamic plan details.
checkout.session.completed Welcome and onboarding email when a new customer completes checkout.
customer.subscription.trial_will_end Reminder email a few days before trial expires — timed via your backend, triggered via else.events.
Forward Stripe events and let else.events route, render and deliver the right billing email every time.