inbox_beam
Send notification emails to your own inbox from Ruby and Rails without SMTP, a sending domain, or SPF/DKIM/DMARC setup.
If all you want is for your Rails app to drop a contact-form copy, a sign-up alert, or a cron failure into your own inbox, configuring SMTP, a sending domain, SPF, DKIM, DMARC, and a provider like SES, SendGrid, or Postmark is a lot of deliverability overhead for mail only you will read.
inbox_beam skips all of it. It uses the IMAP APPEND command to write the
message straight into your mailbox. No SMTP, no sending domain, no
deliverability — the email shows up unread and searchable, and nothing was sent.
It ships as a plain Ruby client and as a drop-in Action Mailer delivery method, so your existing Rails mailers can land in your inbox unchanged.
Install
# Gemfile
gem "inbox_beam"bundle install
# or
gem install inbox_beamRuby >= 2.7.
Rails / Action Mailer
The delivery method is registered automatically when the gem loads. Point Action Mailer at it, and every mailer you already have appends to your inbox instead of sending over SMTP — no other code changes.
1. Configure the delivery method
# config/environments/production.rb
config.action_mailer.delivery_method = :inbox_beam
config.action_mailer.inbox_beam_settings = {
host: "imap.gmail.com",
auth: { user: "you@example.com", pass: ENV["IMAP_APP_PASSWORD"] },
mailbox: "INBOX" # optional, defaults to "INBOX"
}Keep secrets out of source. With Rails encrypted credentials:
config.action_mailer.inbox_beam_settings = {
host: "imap.gmail.com",
auth: {
user: Rails.application.credentials.dig(:imap, :user),
pass: Rails.application.credentials.dig(:imap, :app_password)
}
}inbox_beam_settings accepts:
| Key | Default | Notes |
|---|---|---|
host: |
— | IMAP host, e.g. imap.gmail.com. Required.
|
auth: |
— |
{ user:, pass: } (app password) or { user:, access_token: } (OAuth2). Required.
|
port: |
993 |
|
ssl: |
true |
Use TLS. |
mailbox: |
"INBOX" |
Target mailbox or label. |
unread: |
true |
Leave the appended message unread. |
The mailer's own mail(from:, to:, subject:) supplies the headers and body, so
the rendered message lands in your inbox exactly as Action Mailer built it.
2. Write a mailer (or use one you already have)
# app/mailers/notification_mailer.rb
class NotificationMailer < ApplicationMailer
def contact_form(submission)
@submission = submission
mail(
to: "you@example.com",
subject: "[Contact] #{submission.name}"
)
end
end<%# app/views/notification_mailer/contact_form.html.erb %>
<h1>New contact-form submission</h1>
<p><strong>From:</strong> <%= @submission.name %> (<%= @submission.email %>)</p>
<p><%= @submission.message %></p>3. Deliver it
NotificationMailer.contact_form(submission).deliver_later
# → appended to your inbox, unread and searchable. No SMTP, no SPF/DKIM/DMARC.That's the whole integration — deliver_now / deliver_later work as usual, and
nothing is sent over the wire to another server.
Per-environment tip
Use :inbox_beam only where you want notifications to reach your own inbox
(production, staging). Leave :test in the test environment and :letter_opener
or :file in development as you normally would.
Plain Ruby
require "inbox_beam"
beam = InboxBeam::Client.new(
host: "imap.gmail.com",
auth: { user: "you@example.com", pass: ENV["IMAP_APP_PASSWORD"] }
)
beam.beam(subject: "New contact", text: "someone submitted the form")HTML plus text produces a multipart/alternative message:
beam.beam(
subject: "Weekly report",
text: "Signups: 42",
html: "<h1>Weekly report</h1><p>Signups: 42</p>"
)Override the target mailbox per call:
beam.beam(subject: "Cron failed", text: "nightly-export exited 1", mailbox: "Alerts")This is not email delivery
inbox_beam appends to a mailbox you already control. It does not send mail and cannot reach anyone else. To email a user or customer, use SMTP or a delivery API. It keeps no send log and the date is set by the client, so don't use it where an audit trail matters. It's for your own notifications.
Gmail setup
- Enable IMAP in Gmail settings.
- Turn on 2-Step Verification and create an App Password.
- Use it as
auth[:pass].
Workspace admins can disable app passwords. In that case pass an OAuth2 token as
auth[:access_token] instead of pass (the client authenticates with XOAUTH2).
A dedicated notify@ account that forwards to you is safer than your primary
account's credentials on a server.
API
InboxBeam::Client.new(...)
| Option | Default | Notes |
|---|---|---|
host: |
— | IMAP host, e.g. imap.gmail.com. |
auth: |
— |
{ user:, pass: } (app password) or { user:, access_token: } (OAuth2). |
port: |
993 |
|
ssl: |
true |
Use TLS. |
mailbox: |
"INBOX" |
Target mailbox or label. |
from: |
auth[:user] |
Default From. |
to: |
auth[:user] |
Default To. |
unread: |
true |
Leave appended messages unread. |
subject_prefix: |
nil |
Prepended to every subject. |
client.beam(...)
Keyword args: subject: (required), text:, html:, from:, to:,
mailbox:, unread:, date:. Per-call values override the constructor
defaults. Returns an InboxBeam::Result with mailbox, uid, and
uid_validity (uid comes from the server's APPENDUID response).
InboxBeam::Message.build(...)
The RFC 5322 builder is available on its own if you want the raw message without the IMAP connection. Zero dependencies, UTF-8 safe.
raw = InboxBeam::Message.build(from: "a@x.com", to: "b@x.com", subject: "Hi", text: "Body")How it works
IMAP's APPEND command (RFC 9051 §6.3.12) adds a message to the end of a
mailbox. It's the same command mail clients use to save sent copies and drafts.
inbox_beam builds an RFC 5322 message and appends it over a TLS IMAP connection
using Ruby's standard net/imap — no runtime dependencies of its own.
A TypeScript/Node version is at inbox-beam.
License
MIT