Class: VerifyAnyEmail::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/verifyanyemail/client.rb

Overview

HTTP client for the VerifyAnyEmail API.

Examples:

client = VerifyAnyEmail::Client.new(ENV["VAE_API_KEY"])
result = client.verify("name@example.com")
result.deliverable? # => true

Constant Summary collapse

DEFAULT_BASE_URL =

Default API base URL.

"https://api.verifyany.email"
DEFAULT_TIMEOUT =

Default per-request timeout, in seconds.

30

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(api_key, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT) ⇒ Client

Returns a new instance of Client.

Parameters:

  • api_key (String)

    Your API key (vae_...).

  • base_url (String) (defaults to: DEFAULT_BASE_URL)

    Override the API base URL.

  • timeout (Integer, Float) (defaults to: DEFAULT_TIMEOUT)

    Open/read timeout in seconds.

Raises:

  • (ArgumentError)

    when api_key is missing.



32
33
34
35
36
37
38
# File 'lib/verifyanyemail/client.rb', line 32

def initialize(api_key, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT)
  raise ArgumentError, "api_key is required" if api_key.nil? || api_key.to_s.empty?

  @api_key = api_key
  @base_url = base_url.to_s.sub(%r{/+\z}, "")
  @timeout = timeout
end

Instance Attribute Details

#base_urlString (readonly)

Returns Base URL (no trailing slash).

Returns:

  • (String)

    Base URL (no trailing slash).



23
24
25
# File 'lib/verifyanyemail/client.rb', line 23

def base_url
  @base_url
end

#timeoutInteger, Float (readonly)

Returns Request timeout in seconds.

Returns:

  • (Integer, Float)

    Request timeout in seconds.



26
27
28
# File 'lib/verifyanyemail/client.rb', line 26

def timeout
  @timeout
end

Class Method Details

.secure_compare(a, b) ⇒ Boolean

Constant-time string comparison that is safe when lengths differ.

Uses OpenSSL.fixed_length_secure_compare when available (Ruby 2.6+), falling back to a manual constant-time XOR comparison.

Parameters:

  • a (String)
  • b (String)

Returns:

  • (Boolean)


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/verifyanyemail/client.rb', line 122

def self.secure_compare(a, b)
  a = a.to_s
  b = b.to_s

  if OpenSSL.respond_to?(:fixed_length_secure_compare)
    # fixed_length_secure_compare raises when lengths differ, so guard first.
    return false unless a.bytesize == b.bytesize

    begin
      return OpenSSL.fixed_length_secure_compare(a, b)
    rescue StandardError
      # Fall through to the manual comparison below.
    end
  end

  return false unless a.bytesize == b.bytesize

  diff = 0
  a.bytes.each_with_index do |byte, i|
    diff |= byte ^ b.getbyte(i)
  end
  diff.zero?
end

.verify_webhook_signature(raw_body, signature_header, secret) ⇒ Boolean

Verify a webhook signature using a length-safe, constant-time comparison.

Parameters:

  • raw_body (String)

    The exact raw request body bytes.

  • signature_header (String, nil)

    Value of the X-VAE-Signature header (with or without the sha256= prefix).

  • secret (String)

    Your webhook secret.

Returns:

  • (Boolean)

    true when the signature is valid.



99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/verifyanyemail/client.rb', line 99

def self.verify_webhook_signature(raw_body, signature_header, secret)
  return false if signature_header.nil? || secret.nil? || secret.to_s.empty?

  provided = signature_header.to_s.strip
  provided = provided.sub(/\Asha256=/, "")

  expected = OpenSSL::HMAC.hexdigest(
    OpenSSL::Digest.new("sha256"),
    secret.to_s,
    raw_body.to_s
  )

  secure_compare(expected, provided)
end

Instance Method Details

#get_batch(id) ⇒ Hash

Fetch the status and summary of a batch.

Parameters:

  • id (String)

    The batch id.

Returns:

  • (Hash)

    Parsed response.

Raises:



76
77
78
# File 'lib/verifyanyemail/client.rb', line 76

def get_batch(id)
  request(:get, "/v1/verify/batch/#{escape(id)}")
end

#get_batch_results(id, page: 1, page_size: 100) ⇒ Hash

Fetch a page of batch results.

Parameters:

  • id (String)

    The batch id.

  • page (Integer) (defaults to: 1)

    1-based page number.

  • page_size (Integer) (defaults to: 100)

    Results per page (1-1000).

Returns:

  • (Hash)

    Parsed response with page, pageSize, total, results.

Raises:



87
88
89
90
# File 'lib/verifyanyemail/client.rb', line 87

def get_batch_results(id, page: 1, page_size: 100)
  query = { "page" => page, "pageSize" => page_size }
  request(:get, "/v1/verify/batch/#{escape(id)}/results", query: query)
end

#verify(email, smtp_probe: nil, catch_all_detection: nil) ⇒ VerifyAnyEmail::VerificationResult

Verify a single email address.

Parameters:

  • email (String)

    The address to verify.

  • smtp_probe (Boolean, nil) (defaults to: nil)

    Set false to skip the live SMTP probe.

  • catch_all_detection (Boolean, nil) (defaults to: nil)

    Set false to skip catch-all detection.

Returns:

Raises:



47
48
49
50
51
52
53
54
# File 'lib/verifyanyemail/client.rb', line 47

def verify(email, smtp_probe: nil, catch_all_detection: nil)
  body = { "email" => email }
  body["smtpProbe"] = smtp_probe unless smtp_probe.nil?
  body["catchAllDetection"] = catch_all_detection unless catch_all_detection.nil?

  response = request(:post, "/v1/verify", body: body)
  VerificationResult.new(response["result"] || {})
end

#verify_batch(emails, name: nil, webhook_url: nil) ⇒ Hash

Submit a batch of addresses for asynchronous verification.

Parameters:

  • emails (Array<String>)

    Addresses to verify (at least one).

  • name (String, nil) (defaults to: nil)

    Optional label for the batch.

  • webhook_url (String, nil) (defaults to: nil)

    Optional signed batch.completed callback URL.

Returns:

  • (Hash)

    Parsed response, e.g. { "ok" => true, "batch" => { "id" => ... } }.

Raises:



63
64
65
66
67
68
69
# File 'lib/verifyanyemail/client.rb', line 63

def verify_batch(emails, name: nil, webhook_url: nil)
  body = { "emails" => Array(emails) }
  body["name"] = name unless name.nil?
  body["webhookUrl"] = webhook_url unless webhook_url.nil?

  request(:post, "/v1/verify/batch", body: body)
end