Rails
Send transactional emails from your Ruby on Rails application using the Veil Mail Ruby SDK.
Prerequisites
- Rails 7 or later
- Ruby 3.0+
- A Veil Mail API key
- A verified domain
1. Install the SDK
Add the gem to your Gemfile:
Gemfile
gem 'veilmail'Then run:
bundle install2. 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_xxxxxconfig/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
endconfig/routes.rb
# config/routes.rb
Rails.application.routes.draw do
post "/api/send-welcome", to: "emails#send_welcome"
end4. 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"] }
end5. 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
endconfig/routes.rb
# config/routes.rb
Rails.application.routes.draw do
post "/webhooks/veilmail", to: "webhooks#veilmail"
endImportant: 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
end7. 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