Rails’ custom validators let you write validates :email, work_email: true on your signup model.
1. Credentials
bin/rails credentials:edit
isbusinessemail:
api_key: ibe_live_…
2. The client
# app/services/is_business_email.rb
require "net/http"
require "json"
module IsBusinessEmail
API = URI("https://api.isbusinessemail.com/v1/check")
FAIL_OPEN = { "recommendation" => "allow", "fail_open" => true }.freeze
def self.check(email, policy: "b2b")
req = Net::HTTP::Post.new(API)
req["Authorization"] = "Bearer #{Rails.application.credentials.dig(:isbusinessemail, :api_key)}"
req["Content-Type"] = "application/json"
req.body = { email: email, policy: policy }.to_json
res = Net::HTTP.start(API.host, API.port, use_ssl: true, open_timeout: 2, read_timeout: 3) do |http|
http.request(req)
end
return JSON.parse(res.body) if res.is_a?(Net::HTTPSuccess)
Rails.logger.warn("isBusinessEmail error: HTTP #{res.code}")
FAIL_OPEN
rescue StandardError => e
Rails.logger.warn("isBusinessEmail unreachable: #{e.class}")
FAIL_OPEN
end
end
3. The validator
# app/validators/work_email_validator.rb
class WorkEmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return if value.blank?
verdict = IsBusinessEmail.check(value, policy: options.fetch(:policy, "b2b"))
record.email_verdict = verdict if record.respond_to?(:email_verdict=)
return unless verdict["recommendation"] == "block"
message = +"Please use your work email."
message << " Did you mean #{verdict['did_you_mean']}?" if verdict["did_you_mean"]
record.errors.add(attribute, :work_email_required, message: message)
end
end
4. Use it
# app/models/user.rb
class User < ApplicationRecord
attr_accessor :email_verdict
validates :email, presence: true,
format: { with: URI::MailTo::EMAIL_REGEXP },
work_email: true, on: :create
before_create do
self.email_category = email_verdict&.dig("category")
self.needs_review = email_verdict&.dig("recommendation") == "review"
end
end
on: :create means existing users aren’t re-checked every time they save their profile.
Notes
- Strict mode for some flows:
work_email: { policy: "strict" }. - Validation runs on every
valid?call. If you call it more than once per request, memoize the verdict per email. - Test with test addresses, or stub
IsBusinessEmail.checkwith WebMock or RSpec doubles.
Official docs: Rails Guides (Active Record Validations). See also: Signup form guide.