Courrier: a gem to send emails without SMTP

Every transactional email provider ships a Ruby SDK. Each one has its own client class, its own conventions, its own way of doing the same thing. I got tired of learning yet another API for every project. So I built Courrier.

About 14 months and 73 commits later, Courrier 1.0 is here. It covers 13 email providers and 7 newsletter platforms. There’s a companion gem for Rails apps that adds generators, ActiveJob support, browser-based inbox previews and more.

Star it on GitHub

13 providers, one API

Courrier.configure do |config|
  config.email = {
    provider: :postmark,
    api_key: "your_postmark_api_key"
  }

  config.from = "devs@railsdesigner.com"
end

# courrier/emails/order_email.rb
class OrderEmail < Courrier::Email
  def subject = "Your order is ready!"

  def text
    <<~TEXT
      Thanks for ordering, #{name}!

      Your order ##{order_id} is on its way.
    TEXT
  end

  def html
    <<~HTML
      <h1>Thanks for ordering!</h1>

      <p>Your order ##{order_id} is on its way.</p>
    HTML
  end
end

OrderEmail.deliver to: "customer@railsdesigner.com", name: "Cam", order_id: 42

That’s it. That’s the whole thing. One class, one deliver call, works with any supported provider.

Courrier has three levels of configuration that build on each other. Set defaults globally, override per email class or per delivery. Need to use Mailpace for transactional emails and Postmark for marketing? Easy. Need a provider not on the list? Subclass Courrier::Email::Providers::Base and add it yourself.

Content your way

The example above uses text and html methods. That’s the simplest path and works for most cases.

Prefer markdown? Define a markdown method instead. Courrier detects your markdown engine (kramdown, redcarpet or commonmarker) automatically and renders it to HTML.

Want the same structure as ViewComponent gives you (an ERB template alongside your email class):

courrier/emails/order_email.rb
courrier/emails/order_email.html.erb

Method definitions take precedence over template files, so you can mix approaches. Plain text via a method, HTML via a template. Or both via templates. Whatever fits your mental model.

Layouts wrap your email content the same way Rails layouts wrap views. Define one inline, in a method or in a separate class. And if you only write HTML, Courrier can auto generate the plain text version. 🤖

But wait, there is more

The Result object tells you if delivery succeeded, with access to the raw response data and any errors. Test helpers give you assert_emails_delivered and friends, so you can verify delivery without hitting a real API. before_deliver and after_deliver callbacks let you hook into the lifecycle. Custom HTTP headers (like List-Unsubscribe) go straight into each email class.

There’s also a a newsletter subscription system: Courrier::Subscriber.create and .destroy work with Beehiiv, Buttondown, Kit, Loops, Mailchimp, MailerLite and Brevo.

Rails Courrier

Courrier works with any Ruby app. But if you’re using Rails, there’s a companion gem that adds Rails-specific features.

Generators scaffold everything for you. Run rails generate courrier:install to create the initializer. Pass --provider to pre-fill provider-specific config. Run rails generate courrier:email Order to create an email class.

ActiveJob support means you can send emails in the background with a single method change. Call deliver_later instead of deliver.

Inbox gives you browser-based email previews, like (good ol’) Letter Opener but built into the gem. Mount the engine in your routes and every sent email opens in your browser.

I18n is scoped under courrier.email.<class_name>, so your translations stay organised. The t helper works in email classes and ERB templates.

Instrumentation publishes delivery.courrier and delivery_failed.courrier events through ActiveSupport::Notifications.


I have a few ideas for what is next. Auto redundancy: if your primary provider goes down, Courrier falls back to a backup provider. Baked-in layouts to make your emails a bit fancier (without asking a LLM). Dedicated real email testing tools that go beyond the test helpers. And more delivery methods beyond email (SMS, push notifications). 🤯

A huge thank you to delynn, benedikt, faheemKamboh and JSap0914 for their contributions. ❤️

Give Courrier a try. Star the repo on GitHub. For Rails apps, use rails_courrier. Would love to see what you build with it. 🚀🤘

Product-minded Rails notes

Once a month: straightforward notes on improving UX in Rails—what to simplify, what to measure, and UI/frontend changes that move real usage.

Over to you…

What did you like about this article? Learned something knew? Found something is missing or even broken? 🫣 Let me (and others) know!

Comments are powered by Chirp Form

Want to read me more?