
Railway prevents SMTP outbound connections in the hobby plan which prevents me from using an SMTP service to send emails from my application.
I'm using ZeptoMail from Zoho. Thus, I had to use the HTTP API from ZeptoMail for sending my emails. However, ZeptoMail did not have a Ruby SDK. So, I decided to implement a custom ActionMailer delivery method.
Before building the class, it helps to understand how ActionMailer handles delivery.
ActionMailer is a Rails abstraction for sending emails. It gives you mailer classes, view templates, and delivery methods.
You can register any class as the delivery method with ActionMailer::Base.add_delivery_method. The only requirement for this class is that its constructor should accept a settings hash and implement deliver!(mail).
The mail object is an instance of Mail::Message. It has information such as mail[:from].addresses, mail.subject, mail.html_part, mail.text_part.
We have to create a plain Ruby object in the app/lib directory. We'll use Net::HTTP to send the request.
# app/lib/zeptomail_delivery.rb
class ZeptomailDelivery
attr_accessor :settings
def initialize(settings)
@settings = settings
end
def deliver!(mail)
uri = URI("https://api.zeptomail.in/v1.1/email")
request = Net::HTTP::Post.new(uri)
request["Content-Type"] = "application/json"
request["Accept"] = "application/json"
request["Authorization"] = settings[:api_token]
request.body = {
from: { address: mail.from.first },
to: mail.to.map { |addr| { email_address: { address: addr } } },
subject: mail.subject,
htmlbody: mail.html_part&.body&.decoded,
textbody: mail.text_part&.body&.decoded
}.compact.to_json
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
http.request(request)
end
end
end
Make sure to build a JSON body that matches the expected request format of your mail service's HTTP API.
Now that we have the class ready, let us tell Rails that we have a new delivery method. Add the config for action_mailer in production.rb
# config/environments/production.rb
require Rails.root.join("app/lib/zeptomail_delivery")
# ..existing configs
# ZeptoMail HTTP API configuration
ActionMailer::Base.add_delivery_method :zeptomail, ZeptomailDelivery
config.action_mailer.delivery_method = :zeptomail
config.action_mailer.zeptomail_settings = {
api_token: ENV.fetch("ZEPTOMAIL_API_TOKEN")
}
# ..existing configs
Note that the api_token we pass here will be accessible assettings[:api_token inside the ZeptomailDelivery class.
That is how you add a custom delivery method for ActionMailer.