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"Sponsored By Rails Designer
Add the gem:
bundle add courrierGenerate the configuration file:
bin/rails generate courrier:installThis creates config/initializers/courrier.rb for configuring email providers and default settings.
Generate a new email:
bin/rails generate courrier:email Orderclass 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"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.
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
endWhen 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.
| 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 |
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}"
endCourrier supports these transactional email providers:
Additional functionality to help with development and testing:
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.
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 = trueEmails are automatically cleared with bin/rails tmp:clear, or manually with bin/rails courrier:clear.
Wrap your email content using layouts:
class OrderEmail < Courrier::Email
layout text: "%{content}\n\nThanks for your order!",
html: "<div>\n%{content}\n</div>"
endUsing 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
endUsing 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
endInstead 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
endCreate 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
endAutomatically generate plain text versions from your HTML emails:
config.auto_generate_text = true # Defaults to falseCompose 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
endIn 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
endUse 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)Create your own provider by inheriting from Courrier::Email::Providers::Base:
class CustomProvider < Courrier::Email::Providers::Base
ENDPOINT_URL = ""
def body = ""
def headers = ""
endThen configure it:
config.provider = "CustomProvider"Check the existing providers for implementation examples.
Manage subscribers across popular email marketing platforms:
Courrier.configure do |config|
config.subscriber = {
provider: "buttondown",
api_key: "your_api_key"
}
end# Add a subscriber
subscriber = Courrier::Subscriber.create "subscriber@example.com"
# Remove a subscriber
subscriber = Courrier::Subscriber.destroy "subscriber@example.com"
if subscriber.success?
puts "Subscriber added!"
else
puts "Error: #{subscriber.error}"
end- Beehiiv - requires
publication_id - Buttondown
- Kit (formerly ConvertKit) - requires
form_id - Loops
- Mailchimp - requires
dcandlist_id - MailerLite
Provider-specific configuration:
config.subscriber = {
provider: "mailchimp",
api_key: "your_api_key",
dc: "us19",
list_id: "abc123"
}Create custom providers by inheriting from Courrier::Subscriber::Base:
class CustomSubscriberProvider < Courrier::Subscriber::Base
ENDPOINT_URL = "https://api.example.com/subscribers"
def create(email)
request(:post, ENDPOINT_URL, {"email" => email})
end
def destroy(email)
request(:delete, "#{ENDPOINT_URL}/#{email}")
end
private
def headers
{
"Authorization" => "Bearer #{@api_key}",
"Content-Type" => "application/json"
}
end
endThen configure it:
config.subscriber = {
provider: CustomSubscriberProvider,
api_key: "your_api_key"
}See existing providers for more examples.
Yes! While different in approach, Courrier can fully replace ActionMailer. It's a modern alternative that focuses on API-based delivery. The main difference is in how emails are structured - Courrier uses a more straightforward, class-based approach.
Not at all! While Courrier has some Rails-specific goodies (like the inbox preview feature and generators), it works great with any Ruby application.
No. Courrier is specifically built for API-based email delivery. If SMTP is needed, ActionMailer would be a better choices.
Courrier offers a simpler, more modern approach to sending emails. Each email is a standalone class, configuration is straightforward (typically just only an API key is needed) and it packs few quality-of-life features (like the inbox feature and auto-generate text version).
This project uses Standard for formatting Ruby code. Please make sure to run rake before submitting pull requests.
Courrier is released under the MIT License.
