INTEGRATIONS · Published 2026-10-06 · Updated 2026-10-06 · SecureSMTP

Ruby on Rails

Action Mailer SMTP settings for SecureSMTP.

Action Mailer’s SMTP delivery works with SecureSMTP. Set the SMTP settings once and every mailer uses them. No gem to install.

Setup

Every request needs your site’s API key. Get it when you add the site under Sites (/app/forms/sites) in the dashboard — see Sites and API keys. Keep it in an environment variable on the server:

Shell
export SECURESSMTP_API_KEY="qcs_live_..."
Never put the key in browser JavaScript, a mobile app or a public repository. Anyone who has it can send email as your site.

SMTP settings

config/environments/production.rb
# config/environments/production.rb
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  address: "smtp.securessmtp.com",
  port: 587,
  user_name: "securessmtp",
  password: ENV.fetch("SECURESSMTP_API_KEY"),
  authentication: :plain,
  enable_starttls: true,
  open_timeout: 10,
  read_timeout: 30
}
config.action_mailer.raise_delivery_errors = true

enable_starttls is available from Rails 7.0. On older versions use enable_starttls_auto: true. To use port 465 instead:

config/environments/production.rb
config.action_mailer.smtp_settings = {
  address: "smtp.securessmtp.com",
  port: 465,
  user_name: "securessmtp",
  password: ENV.fetch("SECURESSMTP_API_KEY"),
  authentication: :plain,
  tls: true
}
In relay mode (no verified domain) the From address is replaced: the email goes out from [email protected] with your From name, and the From address you set becomes the Reply-To. Verify your domain and the email goes out from your own address. See Domains and DNS.

Send an email

Ruby
# app/mailers/order_mailer.rb
class OrderMailer < ApplicationMailer
  default from: "Acme Store <[email protected]>"

  def shipped(order)
    @order = order
    attachments["invoice-#{order.id}.pdf"] = File.binread(Rails.root.join("invoices", "#{order.id}.pdf"))
    mail(to: order.email, subject: "Your order has shipped")
  end
end

# Send it in the background with Active Job:
OrderMailer.shipped(order).deliver_later

Attachments are sent as usual, up to 15 MB in total per email.

Handle errors

With raise_delivery_errors = true, a refused email raises a Net::SMTP error that includes the reply code:

Ruby
begin
  OrderMailer.shipped(order).deliver_now
rescue Net::SMTPAuthenticationError
  # 535: the password is not a valid API key
rescue Net::SMTPServerBusy => e
  # 4xx, e.g. 450 rate limit: try again later
  Rails.logger.warn("Email deferred: #{e.message}")
rescue Net::SMTPFatalError => e
  # 5xx, e.g. 550 rejected: retrying will not help
  Rails.logger.error("Email rejected: #{e.message}")
end
SMTP replyMeaning
250Accepted.
535Login failed: the password is not a valid API key.
450 4.7.0Rate limit: more than 120 sends from this site in 60 seconds. Retry later.
550 5.7.0Monthly email limit reached.
550 5.7.1Rejected as spam, the site is disabled, or every recipient has unsubscribed.
550 5.1.1Every recipient is on the suppression list.
552 5.3.4Attachments are over 15 MB in total.
4xx 4.3.0Temporary failure. Retry later.

Or call the API

To get the message_id back or use API-only fields such as template, call the API with Net::HTTP from the standard library:

lib/secure_smtp.rb
# lib/secure_smtp.rb
require "json"
require "net/http"

module SecureSMTP
  API_URL = URI("https://securessmtp.com/api/v1/mail/send")

  class Error < StandardError
    attr_reader :reason, :status, :result

    def initialize(reason, status, result)
      super("SecureSMTP: #{reason} (HTTP #{status})")
      @reason = reason
      @status = status
      @result = result
    end
  end

  def self.send_email(message)
    res = Net::HTTP.post(
      API_URL,
      message.to_json,
      "Content-Type" => "application/json",
      "x-securessmtp-api-key" => ENV.fetch("SECURESSMTP_API_KEY")
    )
    result = begin
      JSON.parse(res.body)
    rescue JSON::ParserError
      {}
    end
    unless result["ok"]
      raise Error.new(result["reason"] || result["error"] || "request_failed", res.code.to_i, result)
    end
    result
  end
end

# SecureSMTP.send_email(
#   to: "[email protected]",
#   subject: "Your order has shipped",
#   text: "Your order is on its way.",
#   from: { name: "Acme Store" }
# )
ResponseMeaningWhat to do
200 ok: trueAccepted for sending.Nothing. Keep message_id if you want to find the email later.
200 rate_limitedMore than 120 sends from this site in 60 seconds.Wait a minute, then try again.
200 over_quotaThe account’s monthly email count is used up (relay mode only).Wait for the new month, upgrade, or send from a verified domain.
200 send_failedThe email was not sent. The error field says why.Read error. Do not retry in a loop.
400 invalid_payload, no_body, …The request is wrong. details lists the fields for invalid_payload.Fix the request. Retrying the same request fails the same way.
400 content_flagged, ai_flaggedThe content was judged to be spam.Change the content. See Deliverability.
401 missing_api_key, invalid_api_keyNo key was sent, or the key is wrong or was rotated.Check the environment variable on the server.
403 site_disabledThe site is disabled.See Blocks.
  • Check ok in the body, not only the HTTP status. Rate limit, monthly limit and send failures come back with HTTP 200.
  • There is no idempotency key. If a request times out, the email may already have been sent, and sending it again can deliver it twice.
  • Every reason is listed in the error catalogue.

Next