Action Mailer Basics

This guide covers sending emails from your Rails application.

After reading this guide, you will know:


1 What is Action Mailer?

Action Mailer allows you to send emails from your Rails application. It's one of the two email related components in the Rails framework. The other is Action Mailbox, which deals with receiving emails.

Action Mailer uses classes (called "mailers") and views to create and configure the email to send. Mailers are classes that inherit from ActionMailer::Base. Mailer classes are similar to controller classes. Both have:

  • Instance variables that are accessible in views.
  • The ability to use layouts and partials.
  • The ability to access a params hash.
  • Actions and associated views in app/views.

2 Creating a Mailer and Views

This section will provide a step-by-step guide to sending email with Action Mailer. Here are the details of each step.

2.1 Generate the Mailer

First, you use the "mailer" generator to create the Mailer related classes:

$ bin/rails generate mailer User
create  app/mailers/user_mailer.rb
invoke  erb
create    app/views/user_mailer
invoke  test_unit
create    test/mailers/user_mailer_test.rb
create    test/mailers/previews/user_mailer_preview.rb

Like the UserMailer below, all generated Mailer classes inherit from ApplicationMailer:

# app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
end

The ApplicationMailer class inherits from ActionMailer::Base, and can be used to define attributes common to all Mailers:

# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: "from@example.com"
  layout 'mailer'
end

If you don't want to use a generator, you can also manually add a file to the app/mailers directory. Make sure that your class inherits from ApplicationMailer:

# app/mailers/custom_mailer.rb
class CustomMailer < ApplicationMailer
end

2.2 Edit the Mailer

The UserMailer in app/mailers/user_mailer.rb initially doesn't have any methods. So next, we add methods (aka actions) to the mailer that will send specific emails.

Mailers have methods called "actions" and they use views to structure their content, similar to controllers. While a controller generates HTML content to send back to the client, a Mailer creates a message to be delivered via email.

Let's add a method called welcome_email to the UserMailer, that will send an email to the user's registered email address:

class UserMailer < ApplicationMailer
  default from: "notifications@example.com"

  def welcome_email
    @user = params[:user]
    @url  = "http://example.com/login"
    mail(to: @user.email, subject: "Welcome to My Awesome Site")
  end
end

The method names in mailers do not have to end in _email.

Here is a quick explanation of the Mailer related methods used above:

  • The default method sets default values for all emails sent from this mailer. In this case, we use it to set the :from header value for all messages in this class. This can be overridden on a per-email basis.
  • The mail method creates the actual email message. We use it to specify the values of headers like :to and :subject per email.

There is also the headers method (not used above), which is used to specify email headers with a hash or by calling headers[:field_name] = 'value'.

It is possible to specify an action directly while using the generator like this:

$ bin/rails generate mailer User welcome_email

The above will generate the UserMailer with an empty welcome_email method.

You can also send multiple emails from a single mailer class. It can be convenient to group related emails together. For example, the above UserMailer can have a goodbye_email (and corresponding view) in addition to the welcome_email.

2.3 Create a Mailer View

Next, for the welcome_email action, you'll need to create a matching view in a file called welcome_email.html.erb in the app/views/user_mailer/ directory. Here is a sample HTML template that can be used for the welcome email:

<h1>Welcome to example.com, <%= @user.name %></h1>
<p>
  You have successfully signed up to example.com,
  your username is: <%= @user.login %>.<br>
</p>
<p>
  To login to the site, just follow this link: <%= link_to 'login`, login_url %>.
</p>
<p>Thanks for joining and have a great day!</p>

the above is the content of the <body> tag. It will be embedded in the default mailer layout, which contains the <html> tag. See Mailer layouts for more.

You can also create a text version of the above email and store it in welcome_email.text.erb in the app/views/user_mailer/ directory (notice the .text.erb extension vs. the html.erb). Sending both formats is considered best practice because, in case of HTML rendering issues, the text version can serve as a reliable fallback. Here is a sample text email:

Welcome to example.com, <%= @user.name %>
===============================================

You have successfully signed up to example.com,
your username is: <%= @user.login %>.

To login to the site, just follow this link: <%= @url %>.

Thanks for joining and have a great day!

Notice that in both HTML and text email templates you can use the instance variables @user and @url.

Now, when you call the mail method, Action Mailer will detect the two templates(text and HTML) and automatically generate a multipart/alternative email.

2.4 Call the Mailer

Once you have a mailer class and view set up, the next step is to actually call the mailer method that renders the email view (i.e. sends the email). Mailers can be thought of as another way of rendering views. Controller actions render a view to be sent over the HTTP protocol. Mailer actions render a view and send it through email protocols instead.

Let's see an example of using the UserMailer to send a welcome email when a user is successfully created.

First, let's create a User scaffold:

$ bin/rails generate scaffold user name email login
$ bin/rails db:migrate

Next, we edit the create action in the UserController to send a welcome email when a new user is created. We do this by inserting a call to UserMailer.with(user: @user).welcome_email right after the user is successfully saved.

We use deliver_later to enqueue the email to be sent later. This way, the controller action will continue without waiting for the email sending code to run. The deliver_later method is backed by Active Job.

class UsersController < ApplicationController
  # ...

  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        # Tell the UserMailer to send a welcome email after save
        UserMailer.with(user: @user).welcome_email.deliver_later

        format.html { redirect_to user_url(@user), notice: "User was successfully created." }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # ...
end

Any key-value pair passed to with becomes the params for the Mailer action. For example, with(user: @user, account: @user.account) makes params[:user] and params[:account] available in the Mailer action.

With the above mailer, view, and controller set up, if you create a new User, you can examine the logs to see the welcome email being sent. The log file will show the text and HTML versions being sent, like this:

[ActiveJob] [ActionMailer::MailDeliveryJob] [ec4b3786-b9fc-4b5e-8153-9153095e1cbf] Delivered mail 6661f55087e34_1380c7eb86934d@Bhumis-MacBook-Pro.local.mail (19.9ms)
[ActiveJob] [ActionMailer::MailDeliveryJob] [ec4b3786-b9fc-4b5e-8153-9153095e1cbf] Date: Thu, 06 Jun 2024 12:43:44 -0500
From: notifications@example.com
To: test@gmail.com
Message-ID: <6661f55087e34_1380c7eb86934d@Bhumis-MacBook-Pro.local.mail>
Subject: Welcome to My Awesome Site
Mime-Version: 1.0
Content-Type: multipart/alternative;
 boundary="--==_mimepart_6661f55086194_1380c7eb869259";
 charset=UTF-8
Content-Transfer-Encoding: 7bit


----==_mimepart_6661f55086194_1380c7eb869259
Content-Type: text/plain;

...

----==_mimepart_6661f55086194_1380c7eb869259
Content-Type: text/html;

...

You can also call the mailer from the Rails console and send emails, perhaps useful as a test before you have a controller action set up. The below will send the same welcome_email as above:

irb> user = User.first
irb> UserMailer.with(user: user).welcome_email.deliver_later

If you want to send emails right away (from a cronjob for example) you can call deliver_now:

class SendWeeklySummary
  def run
    User.find_each do |user|
      UserMailer.with(user: user).weekly_summary.deliver_now
    end
  end
end

A method like weekly_summary from UserMailer would return an ActionMailer::MessageDelivery object, which has the methods deliver_now or deliver_later to send itself now or later. The ActionMailer::MessageDelivery object is a wrapper around a Mail::Message. If you want to inspect, alter, or do anything else with the Mail::Message object you can access it with the message method on the ActionMailer::MessageDelivery object.

Here is an example of the MessageDelivery object from the Rails console example above:

irb> UserMailer.with(user: user).weekly_summary
#<ActionMailer::MailDeliveryJob:0x00007f84cb0367c0
 @_halted_callback_hook_called=nil,
 @_scheduled_at_time=nil,
 @arguments=
  ["UserMailer",
   "welcome_email",
   "deliver_now",
   {:params=>
     {:user=>
       #<User:0x00007f84c9327198
        id: 1,
        name: "Bhumi",
        email: "hi@gmail.com",
        login: "Bhumi",
        created_at: Thu, 06 Jun 2024 17:43:44.424064000 UTC +00:00,
        updated_at: Thu, 06 Jun 2024 17:43:44.424064000 UTC +00:00>},
    :args=>[]}],
 @exception_executions={},
 @executions=0,
 @job_id="07747748-59cc-4e88-812a-0d677040cd5a",
 @priority=nil,

3 Multipart Emails and Attachments

The multipart MIME type represents a document that's comprised of multiple component parts, each of which may have its own individual MIME type (such as the text/html and text/plain). The multipart type encapsulates sending multiple files together in one transaction such as attaching multiple files to an email for example.

3.1 Adding Attachments

You can add an attachment with Action Mailer by passing the file name and content to the attachments method. Action Mailer will automatically guess the mime_type, set the encoding, and create the attachment.

attachments['filename.jpg'] = File.read('/path/to/filename.jpg')

When the mail method is triggered, it will send a multipart email with an attachment, properly nested with the top level being multipart/mixed and the first part being a multipart/alternative containing the plain text and HTML email messages.

The other way to send attachments is to specify the file name, MIME-type and encoding headers, and content. Action Mailer will use the settings you pass in.

encoded_content = SpecialEncode(File.read('/path/to/filename.jpg'))
attachments['filename.jpg'] = {
  mime_type: 'application/gzip',
  encoding: 'SpecialEncoding',
  content: encoded_content
}

Action Mailer will automatically Base64 encode an attachment. If you want something different, you can encode your content and pass in the encoded content as well as the encoding in a Hash to the attachments method. If you specify an encoding, Action Mailer will not try to Base64 encode the attachment.

3.2 Making Inline Attachments

Sometimes, you may want to send an attachment (e.g. image) inline, so it appears within the email body.

In order to do this, first, you turn an attachment into an inline attachment by calling #inline:

def welcome
  attachments.inline['image.jpg'] = File.read('/path/to/image.jpg')
end

Then in the view, you can reference attachments as a hash and specify the file you want to show inline. You can call url on the hash and pass the result into the image_tag method:

<p>Hello there, this is the image you requested:</p>

<%= image_tag attachments['image.jpg'].url %>

Since this is a standard call to image_tag you can pass in an options hash after the attachment URL as well:

<p>Hello there, this is our image</p>

<%= image_tag attachments['image.jpg'].url, alt: 'My Photo', class: 'photos' %>

3.3 Multipart Emails

As demonstrated in Create a Mailer View, Action Mailer will automatically send multipart emails if you have different templates for the same action. For example, if you have a UserMailer with welcome_email.text.erb and welcome_email.html.erb in app/views/user_mailer, Action Mailer will automatically send a multipart email with both the HTML and text versions included as separate parts.

The Mail gem has helper methods for making a multipart/alternate email for text/plain and text/html MIME types and you can manually create any other type of MIME email.

The order of the parts getting inserted is determined by the :parts_order inside of the ActionMailer::Base.default method.

Multipart is also used when you send attachments with email.

4 Mailer Views and Layouts

Action Mailer uses view files to specify the content to be sent in emails. Mailer views are located in the app/views/name_of_mailer_class directory by default. Similar to a controller view, the name of the file matches the name of the mailer method.

Mailer views are rendered within a layout, similar to controller views. Mailer layouts are located in app/views/layouts. The default layout is mailer.html.erb and mailer.text.erb. This sections covers various features around mailer views and layouts.

4.1 Configuring Custom View Paths

It is possible to change the default mailer view for your action in various ways, as shown below.

There is a template_path and template_name option to the mail method:

class UserMailer < ApplicationMailer
  default from: 'notifications@example.com'

  def welcome_email
    @user = params[:user]
    @url  = 'http://example.com/login'
    mail(to: @user.email,
         subject: 'Welcome to My Awesome Site',
         template_path: 'notifications',
         template_name: 'hello')
  end
end

The above configures the mail method to look for a template with the name hello in the app/views/notifications directory. You can also specify an array of paths for template_path, and they will be searched in order.

If you need more flexibility, you can also pass a block and render a specific template. You can also render plain text inline without using a template file:

class UserMailer < ApplicationMailer
  default from: 'notifications@example.com'

  def welcome_email
    @user = params[:user]
    @url  = 'http://example.com/login'
    mail(to: @user.email,
         subject: 'Welcome to My Awesome Site') do |format|
      format.html { render 'another_template' }
      format.text { render plain: 'hello' }
    end
  end
end

This will render the template another_template.html.erb for the HTML part and "hello" for the text part. The render method is the same one used inside of Action Controller, so you can use all the same options, such as :plain, :inline, etc.

Lastly, if you need to render a template located outside of the default app/views/mailer_name/ directory, you can apply the prepend_view_path, like so:

class UserMailer < ApplicationMailer
  prepend_view_path "custom/path/to/mailer/view"

  # This will try to load "custom/path/to/mailer/view/welcome_email" template
  def welcome_email
    # ...
  end
end

There is also an append_view_path method.

4.2 Generating URLs in Action Mailer Views

In order to add URLs to your mailer, you need set the host value to your application's domain first. This is because, unlike controllers, the mailer instance doesn't have any context about the incoming request.

You can configure the default host across the application in config/application.rb:

config.action_mailer.default_url_options = { host: 'example.com' }

Once the host is configured, it is recommended that email views use the *_url with the full URL, and not the *_path helpers with relative URL. Since email clients do not have web request context, *_path helpers have no base URL to form complete web addresses.

For example, instead of:

<%= link_to 'welcome', welcome_path %>

Use:

<%= link_to 'welcome', welcome_url %>

By using the full URL, your links will work correctly in your emails.

4.2.1 Generating URLs with url_for

The url_for helper generates a full URL, by default, in templates.

If you haven't configured the :host option globally, you'll need to pass it to url_for.

<%= url_for(host: 'example.com',
            controller: 'welcome',
            action: 'greeting') %>

4.2.2 Generating URLs with Named Routes

Similar to other URLs, you need to use the *_url variant of named route helpers in emails as well.

You either configure the :host option globally or make sure to pass it to the URL helper:

<%= user_url(@user, host: 'example.com') %>

4.3 Adding Images in Action Mailer Views

In order to use the image_tag helper in emails, you need to specify the :asset_host parameter. This is because a mailer instance doesn't have any context about the incoming request.

Usually the :asset_host is consistent across the application, so you can configure it globally in config/application.rb:

config.action_mailer.asset_host = 'http://example.com'

Because we can't infer the protocol from the request, you'll need to specify a protocol such as http:// or https:// in the :asset_host config.

Now you can display an image inside your email.

<%= image_tag 'image.jpg' %>

4.4 Caching Mailer View

You can perform fragment caching in mailer views, similar to application views, using the cache method.

<% cache do %>
  <%= @company.name %>
<% end %>

And to use this feature, you need to enable it in your application's config/environments/*.rb file:

config.action_mailer.perform_caching = true

Fragment caching is also supported in multipart emails. Read more about caching in the Rails caching guide.

4.5 Action Mailer Layouts

Just like controller layouts, you can also have mailer layouts. Mailer layouts are located in app/views/layouts. Here is the default layout:

# app/views/layouts/mailer.html.erb
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <style>
      /* Email styles need to be inline */
    </style>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

The above layout is in a file mailer.html.erb. The default layout name is specified in the ApplicationMailer, as we saw earlier with the line layout "mailer" in the Generate Mailer section. Similar to controller layouts, you use yield to render the mailer view inside the layout.

To use a different layout for a given mailer, call layout:

class UserMailer < ApplicationMailer
  layout 'awesome' # Use awesome.(html|text).erb as the layout
end

To use a specific layout for a given email, you can pass in a layout: 'layout_name' option to the render call inside the format block:

class UserMailer < ApplicationMailer
  def welcome_email
    mail(to: params[:user].email) do |format|
      format.html { render layout: 'my_layout' }
      format.text
    end
  end
end

The above will render the HTML part using the my_layout.html.erb file and the text part with the usual user_mailer.text.erb file.

5 Sending Email

5.1 Sending Email to Multiple Recipients

It is possible to send an email to more than one recipient by setting the :to field to a list of email addresses. The list of emails can be an array or a single string with the addresses separated by commas.

For example, to inform all admins of a new registration:

class AdminMailer < ApplicationMailer
  default to: -> { Admin.pluck(:email) },
          from: 'notification@example.com'

  def new_registration(user)
    @user = user
    mail(subject: "New User Signup: #{@user.email}")
  end
end

The same format can be used to add multiple carbon copy (cc) and blind carbon copy (bcc) recipients, by setting the :cc and :bcc keys respectively (similarly to the :to field).

5.2 Sending Email with Name

It's possible to show the name, in addition to the email address, of the person who receives the email or sends the email.

To show the name of the person when they receive the email, you can use email_address_with_name method in to::

def welcome_email
  @user = params[:user]
  mail(
    to: email_address_with_name(@user.email, @user.name),
    subject: 'Welcome to My Awesome Site'
  )
end

The same method in from: works to display the name of the sender:

class UserMailer < ApplicationMailer
  default from: email_address_with_name('notification@example.com', 'Example Company Notifications')
end

If the name is blank (nil or empty string), it returns the email address.

5.3 Sending Email with Subject Translation

If you don't pass a subject to the mail method, Action Mailer will try to find it in your translations. See the Internationalization Guide for more.

5.4 Sending Emails without Template Rendering

There may be cases in which you want to skip the template rendering step and instead supply the email body as a string. You can achieve this using the :body option. Remember to set the :content_type option, such as setting it to text/html below. Rails will default to text/plain as the content type.

class UserMailer < ApplicationMailer
  def welcome_email
    mail(to: params[:user].email,
         body: params[:email_body],
         content_type: "text/html",
         subject: "Already rendered!")
  end
end

5.5 Sending Emails with Dynamic Delivery Options

If you wish to override the default delivery configuration (e.g. SMTP credentials) while delivering emails, you can do this using delivery_method_options in the mailer action.

class UserMailer < ApplicationMailer
  def welcome_email
    @user = params[:user]
    @url  = user_url(@user)
    delivery_options = { user_name: params[:company].smtp_user,
                         password: params[:company].smtp_password,
                         address: params[:company].smtp_host }
    mail(to: @user.email,
         subject: "Please see the Terms and Conditions attached",
         delivery_method_options: delivery_options)
  end
end

6 Action Mailer Callbacks

Action Mailer allows for you to specify a before_action, after_action, and around_action to configure the message, and before_deliver, after_deliver and around_deliver to control the delivery.

Callbacks can be specified with a block or a symbol representing a method name in the mailer class, similar to other callbacks (in controllers or models).

Here are some examples of when you may use one of these callbacks with mailers.

6.1 before_action

You can use a before_action to set instance variables, populate the mail object with defaults, or insert default headers and attachments.

class InvitationsMailer < ApplicationMailer
  before_action :set_inviter_and_invitee
  before_action { @account = params[:inviter].account }

  default to:       -> { @invitee.email_address },
          from:     -> { common_address(@inviter) },
          reply_to: -> { @inviter.email_address_with_name }

  def account_invitation
    mail subject: "#{@inviter.name} invited you to their Basecamp (#{@account.name})"
  end

  def project_invitation
    @project    = params[:project]
    @summarizer = ProjectInvitationSummarizer.new(@project.bucket)

    mail subject: "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})"
  end

  private
    def set_inviter_and_invitee
      @inviter = params[:inviter]
      @invitee = params[:invitee]
    end
end

6.2 after_action

You can use an after_action callback with a similar setup as a before_action but also have access to instance variables that were set in your mailer action.

You can also use an after_action to override delivery method settings by updating mail.delivery_method.settings.

class UserMailer < ApplicationMailer
  before_action { @business, @user = params[:business], params[:user] }

  after_action :set_delivery_options,
               :prevent_delivery_to_guests,
               :set_business_headers

  def feedback_message
  end

  def campaign_message
  end

  private
    def set_delivery_options
      # You have access to the mail instance,
      # @business and @user instance variables here
      if @business && @business.has_smtp_settings?
        mail.delivery_method.settings.merge!(@business.smtp_settings)
      end
    end

    def prevent_delivery_to_guests
      if @user && @user.guest?
        mail.perform_deliveries = false
      end
    end

    def set_business_headers
      if @business
        headers["X-SMTPAPI-CATEGORY"] = @business.code
      end
    end
end

6.3 after_deliver

You could use an after_deliver to record the delivery of the message. It also allows observer/interceptor-like behaviors, but with access to the full mailer context.

class UserMailer < ApplicationMailer
  after_deliver :mark_delivered
  before_deliver :sandbox_staging
  after_deliver :observe_delivery

  def feedback_message
    @feedback = params[:feedback]
  end

  private
    def mark_delivered
      params[:feedback].touch(:delivered_at)
    end

    # An Interceptor alternative.
    def sandbox_staging
      message.to = ['sandbox@example.com'] if Rails.env.staging?
    end

    # A callback has more context than the comparable Observer example.
    def observe_delivery
      EmailDelivery.log(message, self.class, action_name, params)
    end
end

Mailer callbacks abort further processing if body is set to a non-nil value. before_deliver can abort with throw :abort.

7 Action Mailer View Helpers

Action Mailer views have access to most of the same helpers as regular views.

There are also some Action Mailer-specific helper methods available in ActionMailer::MailHelper. For example, these allow accessing the mailer instance from your view with mailer, and accessing the message as message:

<%= stylesheet_link_tag mailer.name.underscore %>
<h1><%= message.subject %></h1>

8 Action Mailer Configuration

This section shows some example configurations for Action Mailer.

For more details on the various configuration options, see the Configuring Rails Applications guide. You can specify configuration options in environment specific files such as production.rb.

8.1 Example Action Mailer Configuration

Here is an example using the :sendmail delivery method, added to a config/environments/$RAILS_ENV.rb file:

config.action_mailer.delivery_method = :sendmail
# Defaults to:
# config.action_mailer.sendmail_settings = {
#   location: '/usr/sbin/sendmail',
#   arguments: %w[ -i ]
# }
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_options = { from: 'no-reply@example.com' }

8.2 Action Mailer Configuration for Gmail

Add this to your config/environments/$RAILS_ENV.rb file to send via Gmail:

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  address:         'smtp.gmail.com',
  port:            587,
  domain:          'example.com',
  user_name:       Rails.application.credentials.dig(:smtp, :user_name),
  password:        Rails.application.credentials.dig(:smtp, :password),
  authentication:  'plain',
  enable_starttls: true,
  open_timeout:    5,
  read_timeout:    5 }

Google blocks sign-ins from apps it deems less secure. You can change your Gmail settings to allow the attempts. If your Gmail account has 2-factor authentication enabled, then you will need to set an app password and use that instead of your regular password.

9 Previewing and Testing Mailers

You can find detailed instructions on how to test your mailers in the testing guide.

9.1 Previewing Emails

You can preview rendered email templates visually by visiting a special Action Mailer preview URL. To set up a preview for UserMailer, create a class named UserMailerPreview in the test/mailers/previews/ directory. To see the preview of welcome_email from UserMailer, implement a method that has the same name in UserMailerPreview and call UserMailer.welcome_email:

class UserMailerPreview < ActionMailer::Preview
  def welcome_email
    UserMailer.with(user: User.first).welcome_email
  end
end

Now the preview will be available at http://localhost:3000/rails/mailers/user_mailer/welcome_email.

If you change something in the mailer view at app/views/user_mailer/welcome_email.html.erb or the mailer itself, the preview will automatically be updated. A list of previews are also available in http://localhost:3000/rails/mailers.

By default, these preview classes live in test/mailers/previews. This can be configured using the preview_paths option. For example, if you want to add lib/mailer_previews to it, you can configure it in config/application.rb:

config.action_mailer.preview_paths << "#{Rails.root}/lib/mailer_previews"

9.2 Rescuing Errors

Rescue blocks inside of a mailer method cannot rescue errors that occur outside of rendering. For example, record deserialization errors in a background job, or errors from a third-party mail delivery service.

To rescue errors that occur during any part of the mailing process, use rescue_from:

class NotifierMailer < ApplicationMailer
  rescue_from ActiveJob::DeserializationError do
    # ...
  end

  rescue_from "SomeThirdPartyService::ApiError" do
    # ...
  end

  def notify(recipient)
    mail(to: recipient, subject: "Notification")
  end
end

10 Intercepting and Observing Emails

Action Mailer provides hooks into the Mail observer and interceptor methods. These allow you to register classes that are called during the mail delivery life cycle of every email sent.

10.1 Intercepting Emails

Interceptors allow you to make modifications to emails before they are handed off to the delivery agents. An interceptor class must implement the .delivering_email(message) method which will be called before the email is sent.

class SandboxEmailInterceptor
  def self.delivering_email(message)
    message.to = ['sandbox@example.com']
  end
end

The interceptor needs to be registered using the interceptors config option. You can do this in an initializer file like config/initializers/mail_interceptors.rb:

Rails.application.configure do
  if Rails.env.staging?
    config.action_mailer.interceptors = %w[SandboxEmailInterceptor]
  end
end

The example above uses a custom environment called "staging" for a production-like server but for testing purposes. You can read Creating Rails Environments for more information about custom Rails environments.

10.2 Observing Emails

Observers give you access to the email message after it has been sent. An observer class must implement the :delivered_email(message) method, which will be called after the email is sent.

class EmailDeliveryObserver
  def self.delivered_email(message)
    EmailDelivery.log(message)
  end
end

Similar to interceptors, you must register observers using the observers config option. You can do this in an initializer file like config/initializers/mail_observers.rb:

Rails.application.configure do
  config.action_mailer.observers = %w[EmailDeliveryObserver]
end

Feedback

You're encouraged to help improve the quality of this guide.

Please contribute if you see any typos or factual errors. To get started, you can read our documentation contributions section.

You may also find incomplete content or stuff that is not up to date. Please do add any missing documentation for main. Make sure to check Edge Guides first to verify if the issues are already fixed or not on the main branch. Check the Ruby on Rails Guides Guidelines for style and conventions.

If for whatever reason you spot something to fix but cannot patch it yourself, please open an issue.

And last but not least, any kind of discussion regarding Ruby on Rails documentation is very welcome on the official Ruby on Rails Forum.