Docs · Building Features

Sending Email

The add-mailer skill, Resend versus letter_opener, and async delivery.

Email is already wired up end to end: Resend in production, letter_opener in development (so messages open in your browser instead of going anywhere), and the :test delivery method in specs. Adding a new transactional email means adding the message itself, not any of the plumbing around it. The fastest way is the add-mailer skill in Claude Code, using app/mailers/sign_in_code_mailer.rb and its views as the template to copy.

Adding a mailer method

Add a method to an existing mailer, or create a new one:

class InvoiceMailer < ApplicationMailer
  def reminder(invoice)
    @invoice = invoice
    mail(to: invoice.account.owner_email, subject: "Your invoice is due")
  end
end

Keep the default from address (it already reads from config/initializers/app_identity.rb); override it per message only when you genuinely need to.

Views

Every mailer method needs two views: app/views/invoice_mailer/reminder.html.erb and app/views/invoice_mailer/reminder.text.erb. The plain-text version isn't optional decoration; some clients render it, some spam filters weigh its presence, and it's part of what keeps transactional email deliverable.

Sending it

InvoiceMailer.reminder(invoice).deliver_later

deliver_later sends through Solid Queue, off the request cycle, the same background-job mechanism described in Background Jobs. For a delayed send:

InvoiceMailer.reminder(invoice).deliver_later(wait: 5.minutes)

See User#send_welcome_email_later for the pattern this kit uses to fall back to an inline send in development and test, where waiting for a queued job to run isn't always convenient.

Testing and previewing

Write spec/mailers/invoice_mailer_spec.rb, asserting the recipient, subject, and body of the rendered message. Wherever the mailer is triggered, add a have_enqueued_mail assertion at that call site. Optionally, add a preview under spec/mailers/previews/ so you can view the rendered email in letter_opener without triggering the real send path.

Verify

bin/check should be green.

Next

For every other kind of external service besides email, see The Adapter+Fake Pattern.