Rails

Send transactional emails from your Ruby on Rails application using the Veil Mail Ruby SDK.

Prerequisites

1. Install the SDK

Add the gem to your Gemfile:

Gemfile
gem 'veilmail'

Then run:

bundle install

2. Configure the Client

Add your credentials to Rails encrypted credentials or environment variables, then create an initializer:

.env
VEILMAIL_API_KEY=veil_live_xxxxx
VEILMAIL_WEBHOOK_SECRET=whsec_xxxxx
config/initializers/veilmail.rb
require "veilmail"

VEILMAIL = VeilMail::Client.new(api_key: ENV.fetch("VEILMAIL_API_KEY"))

3. Send Your First Email

Add a controller action that sends a transactional email:

app/controllers/emails_controller.rb
class EmailsController < ApplicationController
  skip_before_action :verify_authenticity_token

  def send_welcome
    result = VEILMAIL.emails.send(
      from: "hello@yourdomain.com",
      to: params[:email],
      subject: "Welcome, #{params[:name]}!",
      html: "<h1>Welcome</h1><p>Hi #{params[:name]}, thanks for joining.</p>"
    )

    render json: { emailId: result["id"] }
  end
end
config/routes.rb
# config/routes.rb
Rails.application.routes.draw do
  post "/api/send-welcome", to: "emails#send_welcome"
end

4. Send with a Template

Use templates to manage email content outside your code:

app/controllers/emails_controller.rb
def send_invoice
  due_date = 30.days.from_now.strftime("%m/%d/%Y")

  result = VEILMAIL.emails.send(
    from: "billing@yourdomain.com",
    to: params[:email],
    subject: "Invoice ##{params[:invoice_id]}",
    template_id: "template_xxxxx",
    template_data: {
      invoiceId: params[:invoice_id],
      amount: "$#{format('%.2f', params[:amount])}",
      dueDate: due_date
    }
  )

  render json: { emailId: result["id"] }
end

5. Handle Webhooks

Receive real-time event notifications. Use request.raw_post to get the raw body for signature verification.

app/controllers/webhooks_controller.rb
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def veilmail
    signature = request.headers["X-Veilmail-Signature"]
    payload = request.raw_post
    secret = ENV.fetch("VEILMAIL_WEBHOOK_SECRET")

    expected = OpenSSL::HMAC.hexdigest("SHA256", secret, payload)

    unless signature.present? && ActiveSupport::SecurityUtils.secure_compare(expected, signature)
      return head :unauthorized
    end

    event = JSON.parse(payload)

    case event["type"]
    when "email.delivered"
      Rails.logger.info("Delivered: #{event['data']['emailId']}")
    when "email.bounced"
      Rails.logger.warn("Bounced: #{event['data']['emailId']}")
      # Remove from mailing list or flag user
    when "email.complained"
      Rails.logger.warn("Complaint: #{event['data']['emailId']}")
      # Unsubscribe the user
    when "subscriber.unsubscribed"
      Rails.logger.info("Unsubscribed: #{event['data']['subscriberId']}")
    end

    head :ok
  end
end
config/routes.rb
# config/routes.rb
Rails.application.routes.draw do
  post "/webhooks/veilmail", to: "webhooks#veilmail"
end

Important: Use skip_before_action :verify_authenticity_token on the webhook controller since external services cannot provide a CSRF token. The HMAC signature serves as authentication instead.

6. Manage Subscribers

Add and manage subscribers from your API:

app/controllers/subscribers_controller.rb
class SubscribersController < ApplicationController
  skip_before_action :verify_authenticity_token

  # Add a subscriber when a user signs up
  def subscribe
    subscriber = VEILMAIL.audiences
      .subscribers("audience_xxxxx")
      .add(
        email: params[:email],
        first_name: params[:first_name],
        last_name: params[:last_name],
        metadata: { source: "website" }
      )

    render json: { subscriberId: subscriber["id"] }
  end

  # Unsubscribe
  def unsubscribe
    VEILMAIL.audiences
      .subscribers("audience_xxxxx")
      .remove(params[:subscriber_id])

    render json: { success: true }
  end
end

7. Error Handling

The SDK raises typed exceptions you can rescue and handle:

app/controllers/emails_controller.rb
def send_email
  result = VEILMAIL.emails.send(
    from: "hello@yourdomain.com",
    to: params[:email],
    subject: "Hello",
    html: "<p>Hi!</p>"
  )

  render json: { id: result["id"] }
rescue VeilMail::RateLimitError
  render json: { error: "Rate limited, try again later" }, status: :too_many_requests
rescue VeilMail::VeilMailError => e
  render json: { error: e.message }, status: e.status_code
rescue StandardError
  render json: { error: "Internal error" }, status: :internal_server_error
end