Inbound email in Rails apps with Fuik

Fuik is a Rails engine for handling webhooks that I launched recently. But it can also be used to process inbound emails! 💡

When working with inbound email in Rails using Action Mailbox, there’s a key requirement: managing EML files and blob storage. Once an email arrives at your domain, your email provider captures it and sends the data to you via webhook. You then extract what you need and process (or store) it. Action Mailbox requires: ActiveStorage ánd ActionMailer.

But what if you don’t have a need for ActiveStorage (including its cloud storage setup) or ActionMailer (because you use Courrier).

Most transactional email providers send their inbound emails as webhooks: Postmark, SendGrid, Mailgun and Mailpace. They all POST the email data to a webhook URL you configure.

Fuik is great at capturing just that: it captures those incoming webhooks, stores them in the database and calls your event class to process them. You see all your webhooks in at /webhooks. You can inspect and copy payloads, pull path accessors and generally debug what’s happening.

It is simple to get started:

bundle add fuik
bin/rails generate fuik:install
bin/rails db:migrate

That’s it. Your app now has /webhooks endpoints ready to receive any webhooks. Now to process inbound emails, let’s look at Postmark as an example (though every provider that sends inbound emails as webhooks wil work).

Postmark lets you receive inbound emails by pointing a MX record to their servers and then configure a webhook URL. When an email arrives, the data is POSTed to you. Let’s see how to process it in your app with Fuik.

Generate the class:

bin/rails generate fuik:provider postmark inbound_email

This creates three files.

Now open app/webhooks/postmark/inbound_email.rb. Postmark sends "To", "From", "Subject", "TextBody", and "HtmlBody". Route emails based on the recipient address:

module Postmark
  class InboundEmail < Base
    def process!
      return if to_email.blank?

      case to_email
      when /replies-.*@/
        message = Message.find_by(email_address: to_email)
        message.replies.create!(
          content: text_content,
          from: from_email
        )
      when /support@/
        SupportTicket.create!(
          from: from_email,
          subject: subject,
          body: text_content
        )
      end

      @webhook_event.processed!
    end

    private

    def to_email = payload["To"]

    def from_email = payload["From"]

    def subject = payload["Subject"]

    def text_content = payload["TextBody"]
  end
end

Postmark doesn’t send an event type in the payload (all inbound webhooks are inbound emails), so you set it statically. Use Fuik’s config (app/webhooks/postmark/config.yml) to extract it:

event_type:
  source: static
  value: inbound_email

Point your Postmark inbound webhook to https://yourdomain.com/webhooks/postmark in their dashboard, and emails start flowing in.

In the repo shared, I also added an example for Sendgrid to show another example how to do this.

Any provider with webhooks

Postmark and SendGrid are just examples. Mailgun, Brevo, Mailpace and others all send structured JSON via webhooks. The pattern is the same. Generate a provider, map the payload fields to your private methods, add your business logic and mark the event as processed.

Check out the example repository with both Postmark and SendGrid set up.

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?