Requirements: Ruby 3.2.0 or higher
Installation
Add to your Gemfile:
gem "ark-email" , "~> 0.5"
Then run:
Or install directly:
Quick Start
require "ark_email"
# Initialize the client
client = ArkEmail :: Client . new ( api_key: ENV [ "ARK_API_KEY" ])
# Send an email
email = client. emails . send_ (
from: "Security <[email protected] >" ,
to: [ "[email protected] " ],
subject: "Reset your password" ,
html: "<h1>Password Reset</h1><p>Click the link below to reset your password.</p>" ,
text: "Password Reset \n\n Click the link below to reset your password."
)
puts "Email ID: #{ email. data . id } "
puts "Status: #{ email. data . status } "
Ruby uses send_ (with underscore) because send is a reserved method in Ruby’s Object class.
Configuration
Client Options
require "ark_email"
client = ArkEmail :: Client . new (
api_key: "ark_..." , # Required (or set ARK_API_KEY env var)
timeout: 60 , # Request timeout in seconds (default: 60)
max_retries: 2 , # Number of retry attempts (default: 2)
base_url: "https://api.arkhq.io/v1" # API base URL (rarely needed)
)
Environment Variables
export ARK_API_KEY = "ark_live_..."
API Reference
Emails
Send a single email. email = client. emails . send_ (
from: "[email protected] " ,
to: [ "[email protected] " ],
subject: "Welcome!" ,
html: "<h1>Hello</h1>" ,
text: "Hello" , # Optional but recommended
cc: [ "[email protected] " ], # Optional
bcc: [ "[email protected] " ], # Optional
reply_to: "[email protected] " , # Optional
tags: [ "welcome" , "onboarding" ], # Optional
metadata: { user_id: "123" }, # Optional
track_opens: true , # Optional
track_clicks: true , # Optional
scheduled_at: "2024-01-20T09:00:00Z" , # Optional - ISO 8601
attachments: [ # Optional
{
filename: "invoice.pdf" ,
content: base64_encoded_content,
content_type: "application/pdf"
}
],
headers: { # Optional custom headers
"X-Custom-Header" => "value"
}
)
Returns: SendEmailResponse with data.id, data.status, etc.
Send multiple emails in a single request.
List emails with filtering and pagination. emails = client. emails . list (
page: 1 ,
per_page: 25 ,
status: "sent" , # Optional filter
tag: "welcome" # Optional filter
)
emails. data . each do | email |
puts " #{ email. id } : #{ email. subject } - #{ email. status } "
end
# Pagination info
puts "Page #{ emails. page } of #{ emails. total_pages } "
Get a single email by ID. email = client. emails . retrieve ( "msg_abc123xyz" )
puts "Subject: #{ email. data . subject } "
puts "Status: #{ email. data . status } "
puts "Created: #{ email. data . created_at } "
Get delivery attempts for an email. deliveries = client. emails . get_deliveries ( "msg_abc123xyz" )
deliveries. data . each do | delivery |
puts "Attempt at #{ delivery. timestamp } : #{ delivery. status } "
puts " Error: #{ delivery. error } " if delivery. error
end
Retry a failed email. result = client. emails . retry ( "msg_abc123xyz" )
puts "Retry scheduled: #{ result. data . id } "
Domains
Register a new sending domain. domain = client. domains . create ( name: "mail.yourdomain.com" )
puts "Domain ID: #{ domain. data . id } "
puts "DNS Records to configure:"
domain. data . dns_records . each do | record |
puts " #{ record. type } #{ record. name } -> #{ record. value } "
end
List all domains. domains = client. domains . list
domains. data . each do | domain |
status = domain. verified ? "Verified" : "Pending"
puts " #{ domain. name } : #{ status } "
end
Get domain details. domain = client. domains . retrieve ( "dom_abc123" )
puts "Domain: #{ domain. data . name } "
puts "Verified: #{ domain. data . verified } "
Trigger DNS verification. result = client. domains . verify ( "dom_abc123" )
if result. data . verified
puts "Domain verified successfully!"
else
puts "DNS records not found. Please check your configuration."
end
Remove a domain. client. domains . delete ( "dom_abc123" )
puts "Domain deleted"
Suppressions
Add an email to the suppression list. suppression = client. suppressions . create (
email: "[email protected] " ,
reason: "hard_bounce"
)
Add multiple emails to the suppression list. result = client. suppressions . bulk_create (
suppressions: [
{ email: "[email protected] " , reason: "hard_bounce" },
{ email: "[email protected] " , reason: "complaint" }
]
)
puts "Added #{ result. data . created_count } suppressions"
List suppressed emails. suppressions = client. suppressions . list (
page: 1 ,
per_page: 50 ,
reason: "hard_bounce" # Optional filter
)
suppressions. data . each do | s |
puts " #{ s. email } : #{ s. reason } (added #{ s. created_at } )"
end
Check if an email is suppressed. begin
suppression = client. suppressions . retrieve ( "[email protected] " )
puts "Suppressed: #{ suppression. data . reason } "
rescue ArkEmail :: Errors :: NotFoundError
puts "Email is not suppressed"
end
Remove from suppression list.
Webhooks
Create a webhook endpoint. webhook = client. webhooks . create (
url: "https://yourapp.com/webhooks/ark" ,
events: [ "MessageSent" , "MessageBounced" , "MessageLoaded" ]
)
puts "Webhook ID: #{ webhook. data . id } "
puts "Signing Secret: #{ webhook. data . signing_secret } " # Store this!
List all webhooks. webhooks = client. webhooks . list
webhooks. data . each do | wh |
puts " #{ wh. id } : #{ wh. url } "
puts " Events: #{ wh. events . join ( ', ' ) } "
end
Update webhook configuration. webhook = client. webhooks . update (
"wh_abc123" ,
events: [ "MessageSent" , "MessageBounced" ] # Updated events
)
Send a test event to your webhook. result = client. webhooks . test ( "wh_abc123" , event: "MessageSent" )
puts "Test result: #{ result. data . status } "
Delete a webhook. client. webhooks . delete ( "wh_abc123" )
Tracking
Configure a custom tracking domain. tracking = client. tracking . create ( domain: "track.yourdomain.com" )
puts "Add this CNAME record:"
puts " #{ tracking. data . cname_target } "
List tracking domains. tracking_domains = client. tracking . list
tracking_domains. data . each do | t |
status = t. verified ? "Verified" : "Pending"
puts " #{ t. domain } : #{ status } "
end
Verify tracking domain DNS. result = client. tracking . verify ( "trk_abc123" )
puts "Verified: #{ result. data . verified } "
Remove a tracking domain. client. tracking . delete ( "trk_abc123" )
Error Handling
The SDK throws typed exceptions for different error scenarios:
require "ark_email"
client = ArkEmail :: Client . new
begin
email = client. emails . send_ (
from: "[email protected] " ,
to: [ "invalid" ],
subject: "Test" ,
html: "<p>Test</p>"
)
rescue ArkEmail :: Errors :: BadRequestError => e
puts "Invalid request: #{ e. message } "
puts "Error code: #{ e. code } "
rescue ArkEmail :: Errors :: AuthenticationError
puts "Invalid API key"
rescue ArkEmail :: Errors :: RateLimitError => e
puts "Rate limited. Retry after: #{ e. response . headers [ 'Retry-After' ] } "
rescue ArkEmail :: Errors :: APIConnectionError
puts "Network error - check your connection"
rescue ArkEmail :: Errors :: APIError => e
puts "API error #{ e. status } : #{ e. message } "
end
Exception Hierarchy
ArkEmail::Errors::APIError
├── ArkEmail::Errors::APIConnectionError # Network/connection issues
├── ArkEmail::Errors::APITimeoutError # Request timeout
└── ArkEmail::Errors::APIStatusError # HTTP error responses
├── ArkEmail::Errors::BadRequestError # 400
├── ArkEmail::Errors::AuthenticationError # 401
├── ArkEmail::Errors::PermissionDeniedError # 403
├── ArkEmail::Errors::NotFoundError # 404
├── ArkEmail::Errors::UnprocessableEntityError # 422
├── ArkEmail::Errors::RateLimitError # 429
└── ArkEmail::Errors::InternalServerError # 5xx
Type Safety with Sorbet
The SDK includes comprehensive RBI type definitions for Sorbet:
# typed: strict
require "ark_email"
sig { params ( to: String , subject: String , html: String ). returns ( ArkEmail :: SendEmailResponse ) }
def send_welcome_email ( to , subject , html )
client = ArkEmail :: Client . new
client. emails . send_ (
from: "[email protected] " ,
to: [to],
subject: subject,
html: html
)
end
The types work without requiring sorbet-runtime as a dependency.
Response Objects
Response models support multiple access patterns:
email = client. emails . retrieve ( "msg_abc123" )
# Dot notation
puts email. data . subject
# Bracket notation
puts email[ :data ][ :subject ]
# Pattern matching (Ruby 3.0+)
case email
in { data: { status: "sent" } }
puts "Email was sent!"
in { data: { status: "bounced" } }
puts "Email bounced"
end
# Conversion methods
email. to_h # Convert to Hash
email. to_json # Convert to JSON string
email. to_yaml # Convert to YAML string
Thread Safety
The client is thread-safe and maintains a connection pool of 99 connections. Use a single client instance across your application:
# config/initializers/ark.rb (Rails)
ARK_CLIENT = ArkEmail :: Client . new ( api_key: ENV [ "ARK_API_KEY" ])
# app/services/email_service.rb
class EmailService
def send_welcome ( user )
ARK_CLIENT . emails . send_ (
from: "[email protected] " ,
to: [user. email ],
subject: "Welcome, #{ user. name } !" ,
html: render_template ( "welcome" , user: user)
)
end
end
Concurrent Sending
require "ark_email"
client = ArkEmail :: Client . new
users = User . where ( welcome_sent: false ). limit ( 100 )
threads = users. map do | user |
Thread . new do
client. emails . send_ (
from: "[email protected] " ,
to: [user. email ],
subject: "Welcome!" ,
html: "<h1>Hello, #{ user. name } !</h1>"
)
end
end
threads. each ( & :join )
Framework Integration
Ruby on Rails
# config/initializers/ark.rb
Rails . application . config . ark_client = ArkEmail :: Client . new (
api_key: Rails . application . credentials . ark_api_key
)
# Helper method
module ArkHelper
def ark_client
Rails . application . config . ark_client
end
end
# Include in ApplicationController
class ApplicationController < ActionController::Base
include ArkHelper
end
# app/mailers/user_mailer.rb
class UserMailer
include ArkHelper
def welcome ( user )
ark_client. emails . send_ (
from: "[email protected] " ,
to: [user. email ],
subject: "Welcome to #{ Rails . application . config . app_name } !" ,
html: ApplicationController . render (
template: "user_mailer/welcome" ,
locals: { user: user }
)
)
end
end
Using with ActionMailer Pattern
Create a custom delivery method:
# lib/ark_delivery_method.rb
class ArkDeliveryMethod
def initialize ( settings )
@client = ArkEmail :: Client . new ( api_key: settings[ :api_key ])
end
def deliver! ( mail )
@client . emails . send_ (
from: mail. from . first ,
to: mail. to ,
cc: mail. cc ,
bcc: mail. bcc ,
subject: mail. subject ,
html: mail. html_part &. body &. to_s ,
text: mail. text_part &. body &. to_s || mail. body . to_s
)
end
end
# config/initializers/ark.rb
ActionMailer :: Base . add_delivery_method :ark , ArkDeliveryMethod ,
api_key: Rails . application . credentials . ark_api_key
# config/environments/production.rb
config. action_mailer . delivery_method = :ark
Sinatra
require "sinatra"
require "ark_email"
configure do
set :ark , ArkEmail :: Client . new
end
post "/send-email" do
content_type :json
email = settings. ark . emails . send_ (
from: "[email protected] " ,
to: [params[ :to ]],
subject: params[ :subject ],
html: params[ :html ]
)
{ email_id: email. data . id }. to_json
end
Hanami
# lib/yourapp/providers/ark.rb
Hanami . app . register_provider :ark do
prepare do
require "ark_email"
end
start do
client = ArkEmail :: Client . new ( api_key: ENV [ "ARK_API_KEY" ])
register "ark.client" , client
end
end
# app/actions/emails/send.rb
module YourApp
module Actions
module Emails
class Send < YourApp::Action
include Deps [ "ark.client" ]
def handle ( request , response )
email = ark_client. emails . send_ (
from: "[email protected] " ,
to: [request. params [ :to ]],
subject: request. params [ :subject ],
html: request. params [ :html ]
)
response. body = { email_id: email. data . id }. to_json
end
end
end
end
end
Advanced Usage
Custom HTTP Requests
For undocumented endpoints or custom requests:
response = client. request (
method: :post ,
path: "/custom/endpoint" ,
body: { key: "value" },
query: { param: "value" },
headers: { "X-Custom" => "header" }
)
Idempotency
Use idempotency keys to safely retry requests:
require "securerandom"
idempotency_key = SecureRandom . uuid
# This request can be safely retried with the same key
email = client. emails . send_ (
from: "[email protected] " ,
to: [ "[email protected] " ],
subject: "Order Confirmation" ,
html: "<p>Your order is confirmed.</p>" ,
idempotency_key: idempotency_key
)
Resources