Courrier
API-powered email delivery for Ruby apps. With support for Mailgun, Postmark, Resend and more
Install / Use
/learn @Rails-Designer/CourrierREADME
Courrier
API-powered email delivery and newsletter subscription management for Ruby apps

# Quick example
class OrderEmail < Courrier::Email
def subject = "Here is your order!"
def text = "Thanks for ordering"
def html = "<p>Thanks for ordering</p>"
end
OrderEmail.deliver to: "recipient@railsdesigner.com"
# Manage newsletter subscribers
Courrier::Subscriber.create "subscriber@example.com"
Courrier::Subscriber.destroy "subscriber@example.com"
<a href="https://railsdesigner.com/" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Rails-Designer/courrier/HEAD/.github/logo-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/Rails-Designer/courrier/HEAD/.github/logo-light.svg">
<img alt="Rails Designer logo" src="https://raw.githubusercontent.com/Rails-Designer/courrier/HEAD/.github/logo-light.svg" width="240" style="max-width: 100%;">
</picture>
</a>
Sponsored By Rails Designer
Installation
Add the gem:
bundle add courrier
Generate the configuration file:
bin/rails generate courrier:install
This creates config/initializers/courrier.rb for configuring email providers and default settings.
Usage
Generate a new email:
bin/rails generate courrier:email Order
class OrderEmail < Courrier::Email
def subject = "Here is your order!"
def text
<<~TEXT
text body here
TEXT
end
def html
<<~HTML
html body here
HTML
end
end
# OrderEmail.deliver to: "recipient@railsdesigner.com"
Configuration
Courrier uses a configuration system with three levels (from lowest to highest priority):
- Global configuration
Courrier.configure do |config|
config.email = {
provider: "postmark",
api_key: "xyz"
}
config.from = "devs@railsdesigner.com"
config.default_url_options = { host: "railsdesigner.com" }
# Provider-specific configuration
config.providers.loops.transactional_id = "default-template"
config.providers.mailgun.domain = "notifications.railsdesigner.com"
end
- Email class defaults
class OrderEmail < Courrier::Email
configure from: "orders@railsdesigner.com",
cc: "records@railsdesigner.com",
provider: "mailgun"
end
- Instance options
OrderEmail.deliver to: "recipient@railsdesigner.com",\
from: "shop@railsdesigner.com",\
provider: "sendgrid",\
api_key: "sk_a1b1c3"
Provider and API key settings can be overridden using environment variables (COURRIER_PROVIDER and COURRIER_API_KEY) for both global configuration and email class defaults.
Custom attributes
Besides the standard email attributes (from, to, reply_to, etc.), you can pass any additional attributes that will be available in your email templates:
OrderEmail.deliver to: "recipient@railsdesigner.com",\
download_url: downloads_path(token: "token")
These custom attributes are accessible directly in your email class:
def text
<<~TEXT
#{download_url}
TEXT
end
Result object
When sending an email through Courrier, a Result object is returned that provides information about the delivery attempt. This object offers a simple interface to check the status and access response data.
Available methods
| Method | Return Type | Description |
|:-------|:-----------|:------------|
| success? | Boolean | Returns true if the API request was successful |
| response | Net::HTTP::Response | The raw HTTP response from the email provider |
| data | Hash | Parsed JSON response body from the provider |
| error | Exception | Contains any error that occurred during delivery |
Example
delivery = OrderEmail.deliver(to: "recipient@example.com")
if delivery.success?
puts "Email sent successfully!"
puts "Provider response: #{delivery.data}"
else
puts "Failed to send email: #{delivery.error}"
end
Providers
Courrier supports these transactional email providers:
More Features
Additional functionality to help with development and testing:
Background jobs (Rails only)
Use deliver_later to enqueue delivering using Rails' ActiveJob. You can set
various ActiveJob-supported options in the email class, like so: enqueue queue: "emails", wait: 5.minutes.
queue, enqueue the email on the specified queue;wait, enqueue the email to be delivered with a delay;wait_until, enqueue the email to be delivered at (after) a specific date/time;priority, enqueues the email with the specified priority.
Inbox (Rails only)
You can preview your emails in the inbox:
config.provider = "inbox"
# And add to your routes:
mount Courrier::Engine => "/courrier"
If you want to automatically open every email in your default browser:
config.provider = "inbox"
config.inbox.auto_open = true
Emails are automatically cleared with bin/rails tmp:clear, or manually with bin/rails courrier:clear.
Layout support
Wrap your email content using layouts:
class OrderEmail < Courrier::Email
layout text: "%{content}\n\nThanks for your order!",
html: "<div>\n%{content}\n</div>"
end
Using a method:
class OrderEmail < Courrier::Email
layout html: :html_layout
def html_layout
<<~HTML
<div style='font-family: ui-sans-serif, system-ui;'>
%{content}
</div>
HTML
end
end
Using a separate class:
class OrderEmail < Courrier::Email
layout html: OrderLayout
end
class OrderLayout
self.call
<<~HTML
<div style='font-family: ui-sans-serif, system-ui;'>
%{content}
</div>
HTML
end
end
Template files
Instead of defining text and html methods, you can create ERB template files:
class OrderEmail < Courrier::Email
def subject = "Your order is ready!"
# text and html content will be loaded from template files
end
Create template files alongside your email class:
app/emails/order_email.text.erbapp/emails/order_email.html.erb
Templates have access to all context options and instance variables:
<!-- app/emails/order_email.html.erb -->
<h1>Hello <%= name %>!</h1>
<p>Your order #<%= order_id %> is ready for pickup.</p>
Method definitions take precedence over template files when both exist. You can mix approaches. For example, define text in a method and use a template for the html:
class OrderEmail < Courrier::Email
def subject = "Your order is ready!"
def text = "Hello #{name}! Your order ##{order_id} is ready."
# html will be loaded from app/emails/order_email.html.erb
end
Markdown support
Courrier supports rendering markdown content to HTML when a markdown gem is available. Simply bundle any supported markdown gem (redcarpet, kramdown or commonmarker) and it will be used.
Markdown methods
Define a markdown method in your email class:
class OrderEmail < Courrier::Email
def subject = "Your order is ready!"
def markdown
<<~MARKDOWN
# Hello #{name}!
Your order **##{order_id}** is ready for pickup.
## Order Details
- Item: #{item_name}
- Price: #{price}
MARKDOWN
end
end
Markdown templates
Create markdown template files alongside your email class:
app/emails/order_email.md.erbapp/emails/order_email.markdown.erb
<!-- app/emails/order_email.md.erb -->
# Hello <%= name %>!
Your order **#<%= order_id %>** is ready for pickup.
## Order Details
- Item: <%= item_name %>
- Price: <%= price %>
Method definitions take precedence over template files. You can mix approaches. For example, define text in a method and use a markdown template for HTML content.
Auto-generate text from HTML
Automatically generate plain text versions from your HTML emails:
config.auto_generate_text = true # Defaults to false
Email address helper
Compose email addresses with display names:
class SignupsController < ApplicationController
def create
recipient = email_with_name("devs@railsdesigner.com", "Rails Designer Devs")
WelcomeEmail.deliver to: recipient
end
end
In Plain Ruby Objects:
class Signup
include Courrier::Email::Address
def send_welcome_email(user)
recipient = email_with_name(user.email_address, user.name)
WelcomeEmail.deliver to: recipient
end
end
Logger provider
Use Ruby's built-in Logger for development and testing:
config.provider = "logger" # outputs emails to STDOUT
config.logger = custom_logger # optional: defaults to ::Logger.new($stdout)
Custom providers
Create your own provider by inheriting from Courrier::Email::Providers::Base:
class CustomProvider < Courrier::Email::Providers::Base
ENDPOINT_URL = ""
def body = ""
def headers = ""
end
Then configure it:
config.provider = "CustomProvider"
Check the existing providers for implementation examples.
Newsletter subscriptions
Manage subscribers across popular email marketing platforms:
Courrier.configure do |config|
config.subscriber = {
provider: "buttondown",
api_key: "your_api_key"
}
end
# Add a subscriber
subscri
