Skip to main content
Documentation

Ruby SDK

RubyGems: coffeemail v0.1.0

Complete reference for the official coffeemail gem for Ruby 3.3+. No runtime dependencies, immutable models, safe data, error results, RBS types and webhook verification.

Looking for the Node.js or TypeScript documentation? See the official @coffeemail/node package reference.

Node.js SDK
bundle add coffeemail

Client Initialization

Rails • Sinatra • Hanami • Sidekiq

Without api_key:, the key is read from the COFFEEMAIL_API_KEY environment variable. The client is immutable and can be shared across threads.

CoffeeMail::Client.new(api_key: nil, locale: "pt-BR", timeout: 10, retry_policy: nil, transport: nil)
send_email.rb
require "coffeemail" # Sem api_key:, o SDK lê COFFEEMAIL_API_KEY do ambiente.client = CoffeeMail::Client.new data, error = client.emails.send(  from: "contato@seudominio.com.br",  to: "cliente@gmail.com",  subject: "Bem-vindo ao CoffeeMail!",  html: "<h1>Olá!</h1><p>Sua conta está ativa.</p>",) abort("Erro ao enviar [#{error.code}]: #{error.message}") if error puts "E-mail enfileirado: #{data.id}"

Safe Results and Error Handling

Safe Pattern data, error

Every operation returns a CoffeeMail::Response with data or error. Destructure with data, error = ..., use pattern matching or call unwrap! to raise the typed error.

error_handling.rb
response = client.emails.get(email_id) data, error = response          # desestruturaçãoresponse => { data: }           # pattern matchingemail = response.unwrap!        # levanta CoffeeMail::Error na falha case response.errorwhen CoffeeMail::RateLimitError  sleep(response.error.retry_after_seconds || 60)when CoffeeMail::NotFoundError  puts "E-mail não encontrado (request_id=#{response.error.request_id})"end

Cryptographic Webhook Verification

HMAC SHA-256 (fixed_length_secure_compare)

CoffeeMail::Webhook.from_rack validates the x-webhook-signature header (HMAC SHA-256 of timestamp, idempotency key and raw body) in constant time and rejects deliveries outside the 5-minute window.

coffee_mail_webhooks_controller.rb
class CoffeeMailWebhooksController < ApplicationController  skip_forgery_protection   def create    result = CoffeeMail::Webhook.from_rack(request, secret: ENV.fetch("COFFEEMAIL_WEBHOOK_SECRET"))    return head(:bad_request) if result.failure?     Rails.logger.info("#{result.data.event_type}: #{result.data.data}")    head(:no_content)  endend

Methods Catalog

Reference for the most used operations of the official Ruby client.

Emails

.send— Send a transactional email
client.emails.send(from:, to:, subject: nil, html: nil, text: nil, idempotency_key: nil, sandbox: false, **) -> Response[SendEmailResult]
emails_send.rb
data, error = client.emails.send(  from: { email: "noreply@seudominio.com.br", name: "Minha App" },  to: "cliente@example.com",  subject: "Confirmação do pedido #123",  html: "<h1>Obrigado!</h1><p>Seu pedido está em processamento.</p>",  idempotency_key: "pedido-123",) return warn("Erro [#{error.code}]: #{error.message}") if error puts "E-mail #{data.id} #{data.status}"
.send_batch— Send emails in batch
client.emails.send_batch(items) -> Response[Array[BatchSendItem]]
emails_send_batch.rb
data, error = client.emails.send_batch([  { from: "noreply@seudominio.com.br", to: "ana@example.com", subject: "Oi, Ana", text: "Olá!" },  { from: "noreply@seudominio.com.br", to: "bruno@example.com", subject: "Oi, Bruno", text: "Olá!" },]) data.each { |item| puts item.ok ? item.data.id : item.error }
.get— Get email status
client.emails.get(id) -> Response[EmailDetail]
emails_get.rb
email = client.emails.get("8b0f6a4e-3c2d-4f1a-9b7e-1a2b3c4d5e6f").unwrap!puts "#{email.status} em #{email.delivered_at}"
.list— List sending history with automatic pagination
client.emails.list(status: nil, after: nil, limit: nil, **) -> Response[EmailList]
emails_list.rb
client.emails.pages(status: CoffeeMail::EmailStatus::BOUNCED, limit: 100).each do |page|  break warn(page.error.message) if page.failure?   page.data.emails.each { |email| puts email.id }end

Domains

.create— Register a domain
client.domains.create(name:) -> Response[CreatedDomain]
domains_create.rb
domain, error = client.domains.create(name: "seudominio.com.br") domain&.dkim_records_to_publish&.each { |record| puts "#{record.type} #{record.host} #{record.value}" }
.verify— Verify domain DNS records
client.domains.verify(id) -> Response[DomainVerification]
domains_verify.rb
result = client.domains.verify(domain_id).unwrap!puts "SPF: #{result.checks.spf.ok} · DKIM: #{result.checks.dkim.ok}"

Templates

.create— Create a template
client.templates.create(name:, html:, alias: nil, subject: nil, format: nil, **) -> Response[TemplateDetail]
templates_create.rb
template, error = client.templates.create(  name: "Boas-vindas",  alias: "welcome",  subject: "Bem-vindo, {{nome}}!",  html: "<h1>Olá, {{nome}}!</h1>",)
.preview_by_id— Preview a template with variables
client.templates.preview_by_id(id, variables: nil) -> Response[RenderedTemplate]
templates_preview.rb
preview = client.templates.preview_by_id(template_id, variables: { nome: "Ana" }).unwrap!puts preview.html

Audiences and Contacts

.contacts.create— Add a contact to an audience
client.audiences.contacts.create(audience_id, email:, first_name: nil, last_name: nil, metadata: nil) -> Response[Contact]
audiences_contacts_create.rb
contact, error = client.audiences.contacts.create(  audience_id,  email: "ana@example.com",  first_name: "Ana",  metadata: { plano: "pro" },)

Broadcasts

.create— Create and send a broadcast
client.broadcasts.create(audience_id:, from_email:, template_id: nil, subject: nil, **) -> Response[BroadcastDetail]
broadcasts_create.rb
broadcast = client.broadcasts.create(  audience_id: audience_id,  from_email: "news@seudominio.com.br",  template_id: template_id,).unwrap! client.broadcasts.send(broadcast.id)

Suppressions

.create— Suppress a recipient
client.suppressions.create(email:, reason: nil, expires_at: nil) -> Response[SuppressionDetail]
suppressions_create.rb
client.suppressions.create(email: "nao-enviar@example.com", reason: "manual")

Webhooks

.create— Register a webhook
client.webhooks.create(url:, events:, description: nil) -> Response[WebhookDetail]
webhooks_create.rb
webhook = client.webhooks.create(  url: "https://app.seudominio.com.br/webhooks/coffeemail",  events: [CoffeeMail::Webhook::EventType::EMAIL_DELIVERED, CoffeeMail::Webhook::EventType::EMAIL_BOUNCED],).unwrap! ENV["COFFEEMAIL_WEBHOOK_SECRET"] = webhook.secret
.Webhook.from_rack— Verify a webhook HMAC signature
CoffeeMail::Webhook.from_rack(request, secret:, tolerance: 300) -> Response[Webhook::Event]
webhooks_verify_signature.rb
result = CoffeeMail::Webhook.from_rack(request, secret: ENV.fetch("COFFEEMAIL_WEBHOOK_SECRET"))return head(:bad_request) if result.failure? puts result.data.event_type

Metrics

.get— Get deliverability metrics
client.stats.get(start_date: nil, end_date: nil, granularity: nil, **) -> Response[Stats]
stats_get.rb
stats = client.stats.get(start_date: "2026-10-01", end_date: "2026-10-31", granularity: "day").unwrap!puts "Entrega: #{stats.delivery_rate}% · Abertura: #{stats.open_rate}%"