Send email from Ruby with the Runearc SDK
A gem that uses only the standard library, in any framework. This page installs it, sends an email to a test inbox, reads it back and verifies a signed webhook, with the output of a real run.
gem install runearcNot published yet: this is the command that will work after the release. See the SDK page for the status of every package.


Install
Ruby 3.0 or later. The gem uses only the standard library, and it works in any framework. The commands on this page are the ones that will work once the package is released; none of the five Runearc SDK packages is on a registry yet, and until then the Resend SDK or plain HTTP calls with your API key do the same work. The SDK covers emails, senders, inboxes, messages, threads, conversations, domains, webhooks, keys, suppressions and events. Contacts, lists, campaigns, sequences, templates, forms, pages and analytics are not in it: use the REST API, the CLI or MCP for those.
gem install runearcCreate an API key in the dashboard and export it as RUNEARC_API_KEY. The quickstart below also reads RUNEARC_FROM, a sender address on a domain you have verified, and RUNEARC_BASE_URL to point at another API host. Tested on Ruby 4.0.7.
Send an email
The whole quickstart: create an inbox, send an email to it, wait for it to arrive and check a webhook signature.
# frozen_string_literal: true
require "json"
require "openssl"
require "securerandom"
require "runearc"
runearc = Runearc::Client.new # reads RUNEARC_API_KEY and RUNEARC_BASE_URL
inbox = runearc.inboxes.create(display_name: "quickstart")
puts "inbox #{inbox[:address]}"
subject = "Hello from the Ruby SDK #{SecureRandom.hex(3)}"
sent = runearc.emails.deliver(from: ENV.fetch("RUNEARC_FROM"), to: inbox[:address], subject: subject, text: "It works.")
puts "sent #{sent[:id]}"
waited = runearc.messages.wait(inbox[:id], subject: subject, after: inbox[:created_at], timeout: 30)
abort "no message arrived within 30 seconds" unless waited
puts "received #{waited[:message][:subject]}"
# Sign a sample event the way Runearc does (Standard Webhooks), then verify it.
key = SecureRandom.random_bytes(32)
secret = "whsec_#{[key].pack("m0")}"
payload = JSON.generate(id: "evt_1", type: "message.delivered", data: { email_id: sent[:id] })
timestamp = Time.now.to_i.to_s
signature = [OpenSSL::HMAC.digest("SHA256", key, "evt_1.#{timestamp}.#{payload}")].pack("m0")
headers = { "webhook-id" => "evt_1", "webhook-timestamp" => timestamp, "webhook-signature" => "v1,#{signature}" }
Runearc::Webhook.verify(payload: payload, headers: headers, secret: secret)
puts "webhook verified"
runearc.inboxes.delete(inbox[:id])emails.deliver takes the same fields as the REST API and returns a Hash with symbol keys, including the new email's id. It sends an Idempotency-Key for you, so a retry does not send twice. Passing after: inbox[:created_at] to wait is needed because wait only matches messages received after it starts, and the email can arrive before the wait begins.


The same output as selectable text
inbox inbox-a9h3nb@5exuc9.runearc.email
sent eml_01m70nxwhjj3p9maya98n1ksrs
received Hello from the Ruby SDK f7cec2
webhook verifiedIds and the inbox address are from that one run, on November 9, 2026.
Read an inbox
messages.wait blocks for up to timeout seconds and returns nil when nothing arrived, so check for it. When a message matches, it comes back with the one time codes and links already extracted, which is what a sign-up test needs. The screenshot shows the same inbox in the dashboard. See wait for an email for every filter.
waited = runearc.messages.wait(inbox[:id], subject: subject, after: inbox[:created_at], timeout: 30)
abort "no message arrived within 30 seconds" unless waited
puts "received #{waited[:message][:subject]}"
# waited[:codes] and waited[:links] hold the one time codes and links found in the message

Verify a webhook
The lines in the quickstart verify a sample event the script signs itself with a random secret. It is not a live delivery from Runearc; it shows that Runearc::Webhook.verify accepts a correctly signed body and checks the timestamp, which must be within five minutes. For a real delivery, pass the raw request body and the headers (a Rack env works), as in the receiver tab.
headers = { "webhook-id" => "evt_1", "webhook-timestamp" => timestamp, "webhook-signature" => "v1,#{signature}" }
Runearc::Webhook.verify(payload: payload, headers: headers, secret: secret)
puts "webhook verified"# Sinatra, or any Rack app
post "/runearc/webhook" do
event = Runearc::Webhook.verify(
payload: request.body.read, # the raw body, not re-serialized JSON
headers: request.env,
secret: ENV.fetch("RUNEARC_WEBHOOK_SECRET")
)
puts event[:type], event[:id] # event[:id] is the webhook-id: use it to drop duplicates
status 204
rescue Runearc::WebhookVerificationError
halt 400, "invalid signature"
endThe first tab is from the quickstart we ran. The receiver is from the package README and was not part of that run. The signature headers and the secret format are on the webhooks page.
Errors and retries
Methods return the parsed response and raise a Runearc::Error. It carries status, a stable code to branch on, a kind such as forbidden, a message, and a fix when there is something to do. Narrower classes such as RateLimitError and ConnectionError let you rescue one case.
begin
runearc.emails.deliver(from: "noreply@acme.com", to: "bob@example.com", subject: "Hi", text: "Hi")
rescue Runearc::Error => e
e.status # => 403
e.code # => "sender_not_verified", a stable string to branch on
e.fix # what to do about it, when there is something to do
raise
end
# A stable key makes your own retries safe too, for example from a job queue.
runearc.emails.deliver(idempotency_key: "welcome/#{user.id}", **email)A request is retried up to two times by default, with exponential backoff and jitter, and the wait honours Retry-After. A 429 is always retried; a 5xx, a network error or a timeout is retried only when repeating the request is safe. Other 4xx errors are never retried, and creating an inbox is not repeated after a 5xx. Mail sending gets an Idempotency-Key automatically and every retry reuses it; Runearc remembers keys for 24 hours.
This code is from the package README, not from the quickstart run. Every error code and its fix is listed on the errors page.
Where other SDKs are better
Read on November 9, 2026. Runearc's SDK is new and covers eleven resources; the others have longer histories and, in several cases, more features. We list them in alphabetical order.
Amazon SES
SES covers Ruby through the AWS SDK for Ruby. The page we read was tested on Ruby 1.9.3, so it is dated.
Mailgun
Mailgun lists a Ruby SDK among its SDKs for Go, Node.js, PHP, Java and Python.
Postmark
Postmark lists an official Ruby library that Postmark maintains itself.
Resend
Resend's Ruby gem has docs with examples for attachments, templates, scheduling, audiences, domains and inbound webhooks, which are outside Runearc's eleven resources.
SendGrid
SendGrid's Ruby quickstart is a long-established path: it shows installed gem versions and the 202 status, and has a troubleshooting section.
Maturity and deliverability track record are where Runearc is behind: the providers above have sent email for longer than we have. For the same comparison across the whole product, see Runearc compared with Resend.
Questions
Is the gem on RubyGems yet?
Not yet. The gem is written and tested, and gem install runearc is the command that will work after the release. Until then, Net::HTTP with your API key does the same work.
Which Ruby version does it need?
Ruby 3.0 or later. It uses only net/http, json and openssl from the standard library. We ran the quickstart on Ruby 4.0.7.
Is it an ActionMailer delivery method?
No. It is an API client, not a delivery method for a framework mailer, and it is plain Ruby with no framework code. We ran the quickstart as a plain script.
Is the webhook line in the quickstart a real delivery?
No. The script signs a sample event with a random secret, the way Runearc signs deliveries, and then verifies it. It shows that verification works; it does not show a delivery from Runearc.
Does the SDK cover contacts, campaigns or sequences?
No. It covers emails, senders, inboxes, messages, threads, conversations, domains, webhooks, keys, suppressions and events. Everything else is in the REST API, the CLI and MCP.
Read next
Put Runearc to work
Get inboxes, sending, your own domains and MCP in one workspace. The free plan includes 3 inboxes, 3,000 emails a month and 3 custom domains, with no card.