Email Notifications
This document describes all email notifications sent by the donation platform to donors, and how staff members are notified.
Email Infrastructure
Mailer Class
All donor emails are sent by Donations::DonorMailer, which uses the newsletter layout and exposes three methods:
thank_you(transaction)charge_failed(event_hash)subscription_canceled(subscription)
The sender address comes from the EMAIL_SENDER environment variable (set on ApplicationMailer as default from).
Email Service Provider
- SendGrid is used for email delivery
- Configured via Action Mailer
- Emails sent asynchronously via Sidekiq (
deliver_later, default Active Job queue)
Localization
All emails are localized based on the donor's preferred language, resolved from:
- Transaction/notification metadata (primary)
- Stripe customer metadata
- Browser locale (initial donation)
- Application default (fallback)
Subjects are resolved via I18n (donations.donor_mailer.<action>.subject), translated in all eight supported locales. The HTML templates are single files — locale-specific text comes entirely from the translation files, not from per-locale template variants.
URL Configuration
Email links point to the donations subdomain (DONATIONS_HOST / DONATIONS_SUBDOMAIN), e.g. the subscription management link uses the authenticated /authenticate?authToken=... URL described below.
Donor Emails
1. Thank You Email
Sent when: Successful donation (single or recurring charge)
Trigger: Stripe charge.succeeded webhook → Donations::ProcessStripeEventJob creates a Donations::Transaction; an after_commit callback on the transaction delivers the email.
Mailer Method: Donations::DonorMailer.thank_you(transaction)
Email Content:
- Subject: Localized "Thank you for your donation"
- Donor's name (first_name + last_name)
- Donation amount and currency
- Payment date
- Transaction ID (for reference)
- Link to manage subscription (if recurring)
- Tax deduction information (if applicable)
2. Charge Failed Email
Sent when: Payment attempt fails (recurring subscription)
Trigger: Stripe charge.failed webhook → Donations::ProcessStripeEventJob#process_failed_charge. The email is not sent when the failure is classified as a fraud attempt or when the same payment intent failed more than 3 times in 30 minutes — in that case the payment intent is expired instead (see Error Handling).
Mailer Method: Donations::DonorMailer.charge_failed(event_hash)
Email Content:
- Subject: "Payment failed for your Aleteia donation"
- Donor's name
- Failed amount and currency
- Failure reason (translated): card declined, insufficient funds, card expired, etc.
- Link to update payment method
- Support contact information
3. Subscription Canceled Email
Sent when: Donor cancels a recurring subscription
Trigger: Donations::Subscription#cancel! (from the subscription management page) enqueues Donations::CancelStripeSubscriptionJob, which cancels the subscription in Stripe and then delivers the email.
Mailer Method: Donations::DonorMailer.subscription_canceled(subscription)
Email Content:
- Subject: "Your Aleteia subscription has been canceled"
- Donor's name
- Subscription details (amount, frequency)
- Cancellation date
- Invitation to donate again in the future
- Thank you message
Staff Notifications
There are no staff emails: staff members are notified via Slack.
Every donation event (new donation, new subscription, cancellation, failed charge) creates a Donations::Notification record; an after_commit callback enqueues Donations::SlackNotificationJob, which posts to the channel configured in Setting.donations_slack_channel using the webhook from Setting.donations_slack_webhook_url.
Suspected fraud does not produce an alert notification: the payment intent is expired automatically (Donations::ExpirePaymentIntentJob with reason fraudulent) and a warning is logged. See Error Handling for the detection criteria.
Email Sending Strategy
Asynchronous Delivery
All emails are sent via deliver_later on the default Active Job queue; the webhook processing jobs that trigger them run on the payments queue.
Benefits:
- Non-blocking (doesn't delay webhook response)
- Automatic retry on failure
- Better performance
Retry Logic
If email delivery fails:
- Sidekiq automatically retries with exponential backoff
- Max retries: 25 (default)
- Failures logged to error tracking (Rollbar)
Email Templates
Layout
All donation emails use the newsletter layout. Templates live under app/views/donations/donor_mailer/ (one HTML template per email, shared across locales).
Inline CSS
Email styles are inlined for better email client compatibility by the premailer-rails gem, which processes stylesheets referenced via stylesheet_link_tag automatically at delivery time.
Previewing Emails (in development)
Mailer previews are defined in spec/mailers/previews/donations/donor_preview.rb and cover all three emails, including locale variants and single vs recurring donations.
Access at: http://localhost:3000/rails/mailers/donations/donor
RSpec Tests
Mailer specs live in spec/mailers/donations/donor_mailer_spec.rb and cover headers, content, localization, and the subscription management links.
Subscription Management Links
Emails include authenticated links for donors to manage subscriptions:
Authentication Token:
The token is the donor's signed GlobalID (Donations::Donor#auth_token, generated with to_signed_global_id(expires_in: nil)):
- Cryptographically signed — cannot be forged or tampered with
- Identifies the donor uniquely
- Never expires (the donor can reuse the same link forever)
Link Example:
https://subscriptions.aleteia.org/authenticate?authToken=BAh7CEk...
Clicking the link authenticates the donor and shows the subscription management page.
Email Deliverability
Best Practices Implemented
- SPF/DKIM/DMARC: Configured at DNS level via SendGrid
- Unsubscribe Links: Not included (transactional emails)
- From Address: Uses verified domain (
noreply@aleteia.org) - Reply-To: Can be configured for support email
- List-Unsubscribe Header: Not set (transactional, not marketing)
Bounce Handling
SendGrid handles bounces and sends webhook events:
- Hard bounces: Donor marked as invalid
- Soft bounces: Retry automatically
- Spam reports: Logged and monitored
Monitoring & Analytics
Email Delivery Metrics
Track via SendGrid:
- Delivery rate
- Open rate (if tracking enabled)
- Click rate
- Bounce rate
- Spam complaints
Error Tracking
Email failures are tracked in Rollbar:
- Mailer exceptions
- Template rendering errors
- SendGrid API errors
Next Steps
- Admin Features - Reporting and management tools
- Error Handling - Error scenarios and recovery