Skip to content

onetimesecret/lettermint-ruby

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lettermint Ruby SDK

Gem Version Ruby License: MIT

Unofficial Ruby SDK for the Lettermint transactional email API. Based on the official Python SDK.

Installation

Add to your Gemfile:

gem 'lettermint'

Or install directly:

gem install lettermint

Quick Start

Sending Emails

require 'lettermint'

client = Lettermint::Client.new(api_token: 'your-api-token')

response = client.email
  .from('sender@example.com')
  .to('recipient@example.com')
  .subject('Hello from Ruby')
  .html('<h1>Welcome!</h1>')
  .text('Welcome!')
  .deliver

puts response.message_id

Email Options

Multiple Recipients

client.email
  .from('sender@example.com')
  .to('recipient1@example.com', 'recipient2@example.com')
  .subject('Hello')
  .deliver

CC and BCC

client.email
  .from('sender@example.com')
  .to('recipient@example.com')
  .cc('cc1@example.com', 'cc2@example.com')
  .bcc('bcc@example.com')
  .subject('Hello')
  .deliver

Reply-To

client.email
  .from('sender@example.com')
  .to('recipient@example.com')
  .reply_to('reply@example.com')
  .subject('Hello')
  .deliver

RFC 5322 Addresses

client.email
  .from('John Doe <john@example.com>')
  .to('Jane Doe <jane@example.com>')
  .subject('Hello')
  .deliver

Attachments

require 'base64'

content = Base64.strict_encode64(File.binread('document.pdf'))

# Regular attachment
client.email
  .from('sender@example.com')
  .to('recipient@example.com')
  .subject('Your Document')
  .attach('document.pdf', content)
  .deliver

# Inline attachment (for embedding in HTML)
client.email
  .from('sender@example.com')
  .to('recipient@example.com')
  .subject('Welcome')
  .html('<img src="cid:logo@example.com">')
  .attach('logo.png', logo_content, content_id: 'logo@example.com')
  .deliver

You can also use the EmailAttachment value object:

attachment = Lettermint::EmailAttachment.new(
  filename: 'document.pdf',
  content: content,
  content_id: nil
)

client.email
  .from('sender@example.com')
  .to('recipient@example.com')
  .attach(attachment)
  .deliver

Custom Headers

client.email
  .from('sender@example.com')
  .to('recipient@example.com')
  .subject('Hello')
  .headers({ 'X-Custom-Header' => 'value' })
  .deliver

Metadata and Tags

client.email
  .from('sender@example.com')
  .to('recipient@example.com')
  .subject('Hello')
  .metadata({ campaign_id: '123', user_id: '456' })
  .tag('welcome-campaign')
  .deliver

Routing

client.email
  .from('sender@example.com')
  .to('recipient@example.com')
  .subject('Hello')
  .route('my-route')
  .deliver

Idempotency Key

Prevent duplicate sends when retrying failed requests:

client.email
  .from('sender@example.com')
  .to('recipient@example.com')
  .subject('Hello')
  .idempotency_key('unique-request-id')
  .deliver

Webhook Verification

Verify webhook signatures to ensure authenticity:

require 'lettermint'

webhook = Lettermint::Webhook.new(secret: 'your-webhook-secret')

# Verify using headers (recommended)
payload = webhook.verify_headers(request.headers, request.body)

# Or verify using the signature directly
payload = webhook.verify(
  request.body,
  request.headers['X-Lettermint-Signature']
)

puts payload['event']

Class Method

For one-off verification:

payload = Lettermint::Webhook.verify_signature(
  request.body,
  request.headers['X-Lettermint-Signature'],
  secret: 'your-webhook-secret'
)

Custom Tolerance

Adjust the timestamp tolerance (default: 300 seconds):

webhook = Lettermint::Webhook.new(secret: 'your-webhook-secret', tolerance: 600)

Replay Protection

The webhook verifier validates timestamp freshness and HMAC integrity but does not track previously seen deliveries. Within the tolerance window (default 300 seconds), a captured request could be replayed. Your application should deduplicate incoming webhooks using the x-lettermint-delivery header value as an idempotency key -- for example, by recording processed delivery IDs in a database or cache and rejecting duplicates.

Error Handling

require 'lettermint'

client = Lettermint::Client.new(api_token: 'your-api-token')

begin
  response = client.email
    .from('sender@example.com')
    .to('recipient@example.com')
    .subject('Hello')
    .deliver
rescue Lettermint::AuthenticationError => e
  # 401/403 errors (invalid or revoked token)
  puts "Auth error #{e.status_code}: #{e.message}"
rescue Lettermint::RateLimitError => e
  # 429 errors
  puts "Rate limited, retry after: #{e.retry_after}s"
rescue Lettermint::ValidationError => e
  # 422 errors (e.g., daily limit exceeded)
  puts "Validation error: #{e.error_type}"
  puts "Response: #{e.response_body}"
rescue Lettermint::ClientError => e
  # 400 errors
  puts "Client error: #{e.message}"
rescue Lettermint::TimeoutError => e
  # Request timeout
  puts "Timeout: #{e.message}"
rescue Lettermint::HttpRequestError => e
  # Other HTTP errors
  puts "HTTP error #{e.status_code}: #{e.message}"
end

Webhook Errors

begin
  payload = webhook.verify_headers(headers, body)
rescue Lettermint::InvalidSignatureError
  puts 'Invalid signature - request may be forged'
rescue Lettermint::TimestampToleranceError
  puts 'Timestamp too old - possible replay attack'
rescue Lettermint::WebhookJsonDecodeError => e
  puts "Invalid JSON in payload: #{e.original_exception}"
rescue Lettermint::WebhookVerificationError => e
  puts "Verification failed: #{e.message}"
end

Configuration

Global Configuration

Set defaults once at application boot (e.g., in a Rails initializer):

Lettermint.configure do |config|
  config.base_url = 'https://custom.api.com/v1'
  config.timeout = 60
end

All clients created afterward inherit these defaults:

client = Lettermint::Client.new(api_token: 'your-api-token')
# Uses the global base_url and timeout

Per-Client Overrides

Explicit keyword arguments take precedence over global configuration:

client = Lettermint::Client.new(
  api_token: 'your-api-token',
  base_url: 'https://other.api.com/v1',
  timeout: 10
)

Block Configuration

client = Lettermint::Client.new(api_token: 'your-api-token') do |config|
  config.base_url = 'https://custom.api.com/v1'
  config.timeout = 60
end

Requirements

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages