Ruby SDK

Complete guide to the OneShotMail Ruby SDK -- installation, API reference, Cucumber and RSpec integration.

Installation

gem install oneshot-mail

Or add to your Gemfile:

gem "oneshot-mail"

Then run bundle install. Requires Ruby 3.0+.

Configuration

From environment variable

require "oneshot"

# Reads ONESHOT_API_KEY from environment automatically
client = OneShot::Client.new

Explicit API key

client = OneShot::Client.new(api_key: "osm_live_your_key")

Custom base URL

client = OneShot::Client.new(
  api_key: "osm_live_your_key",
  base_url: "http://localhost:4566/v1"
)

API Reference

create(ttl:, label:, mode:)

Create a new one-shot email address. Returns a Hash with address details.

ParameterTypeDefaultDescription
ttl:Integer3600TTL in seconds.
label:StringnilOptional label for filtering.
mode:String"receive""receive" or "send".
addr = client.create(ttl: 300, label: "signup-test")
puts addr["id"]        # "abc123xyz789def456"
puts addr["address"]   # "abc123xyz789def456@in.oneshotemail.com"
puts addr["status"]    # "waiting"

get(address_id)

Retrieve an address and its current status.

addr = client.get("abc123xyz789def456")
if addr["status"] == "received"
  puts "From: #{addr['email']['from']}"
  puts "Subject: #{addr['email']['subject']}"
end

get_email(address_id)

Retrieve the full parsed email content.

email = client.get_email("abc123xyz789def456")
puts email["from"]         # "noreply@example.com"
puts email["subject"]      # "Verify your account"
puts email["text_body"]    # "Click here to verify..."
puts email["html_body"]    # "<html>...</html>"
puts email["attachments"]  # [{...}, ...]

get_email_raw(address_id)

Retrieve the raw RFC 822 email source.

raw = client.get_email_raw("abc123xyz789def456")
puts raw  # Complete email with headers

download_attachment(address_id, index)

Download an attachment by zero-based index. Returns the raw content.

data = client.download_attachment("abc123xyz789def456", 0)
File.write("invoice.pdf", data)

wait_for_email(address_id, timeout:, poll_interval:)

Poll until an email arrives, with exponential backoff.

ParameterTypeDefaultDescription
timeout:Integer60Max seconds to wait.
poll_interval:Integer2Initial polling interval.

Raises: OneShot::TimeoutError on timeout. OneShot::ExpiredError if the address expires.

email = client.wait_for_email(addr["id"], timeout: 30)
puts email["subject"]

send(to:, subject:, text_body:, html_body:, attachments:, ttl:, label:)

Send a one-shot email from a temporary address.

result = client.send(
  to: "intake@myapp.com",
  subject: "Test invoice",
  text_body: "Please process this invoice.",
  attachments: [
    {
      filename: "invoice.pdf",
      content_type: "application/pdf",
      content_base64: Base64.strict_encode64(File.read("invoice.pdf"))
    }
  ],
  label: "invoice-test"
)
puts result["address"]  # "def456@out.oneshotemail.com"

list(status:, label:, mode:, limit:, cursor:)

List addresses with optional filtering.

addresses = client.list(status: "waiting", label: "ci-run-abc", limit: 10)
addresses.each { |addr| puts "#{addr['id']}: #{addr['status']}" }

delete(address_id)

Delete an address immediately.

client.delete("abc123xyz789def456")

delete_by_label(label)

Bulk-delete all addresses with the given label.

client.delete_by_label("ci-run-abc123")

account

Get account details and usage.

acct = client.account
puts "Plan: #{acct['plan']}"
puts "Receive: #{acct['usage']['receive']['used']}/#{acct['usage']['receive']['limit']}"

health

Check API health.

h = client.health
puts "Status: #{h['status']}"

Error handling

ExceptionHTTP StatusWhen
OneShot::UnauthorizedError401Invalid or missing API key.
OneShot::QuotaExceededError402Quota and credits exhausted.
OneShot::NotFoundError404Address not found or no email yet.
OneShot::ExpiredError410Address expired.
OneShot::RateLimitedError429Rate limit exceeded.
OneShot::TimeoutErrorN/Await_for_email timed out.
OneShot::ErrorAnyBase class for all errors.
begin
  email = client.wait_for_email(addr["id"], timeout: 30)
rescue OneShot::TimeoutError
  puts "Email did not arrive in time"
rescue OneShot::ExpiredError
  puts "Address expired before email arrived"
rescue OneShot::QuotaExceededError
  puts "Quota exceeded -- upgrade your plan"
end

Cucumber integration

This is the flagship integration. Cucumber was born in the Ruby world, and OneShotMail is built for BDD testing.

Directory structure

features/
  signup.feature
  steps/
    signup_steps.rb
  support/
    env.rb
    oneshot_helper.rb
Gemfile

Gemfile

source "https://rubygems.org"

gem "cucumber"
gem "oneshot-mail"
gem "rspec-expectations"  # for should/expect matchers in steps

features/support/env.rb

require "oneshot"
require_relative "oneshot_helper"

# Create a shared client for the test run
ONESHOT_CLIENT = OneShot::Client.new(api_key: ENV.fetch("ONESHOT_API_KEY"))
RUN_ID = "cucumber-#{SecureRandom.hex(4)}"

# Clean up all addresses after the suite
at_exit do
  ONESHOT_CLIENT.delete_by_label(RUN_ID)
end

features/support/oneshot_helper.rb

module OneShotHelper
  def create_email_address(label_suffix = "")
    label = "#{RUN_ID}-#{label_suffix}".sub(/-$/, "")
    addr = ONESHOT_CLIENT.create(ttl: 300, label: label)
    addr
  end

  def wait_for_email_at(address_id, timeout: 30)
    ONESHOT_CLIENT.wait_for_email(address_id, timeout: timeout)
  end
end

World(OneShotHelper)

features/signup.feature

Feature: User signup
  As a new user
  I want to sign up for an account
  So that I can use the application

  Scenario: Successful signup sends a verification email
    Given I have a temporary email address
    When I sign up with that email address
    Then I should receive a verification email
    And the email subject should contain "Verify your account"
    And the email should contain a verification link

  Scenario: Signup with an existing email shows an error
    Given I have a temporary email address
    And that email address is already registered
    When I sign up with that email address
    Then I should see an error message "Email already registered"

features/steps/signup_steps.rb

Given("I have a temporary email address") do
  @address = create_email_address("signup")
  @email_addr = @address["address"]
end

Given("that email address is already registered") do
  # Use your app's API or test helper to create the user
  MyApp::TestHelper.create_user(email: @email_addr)
end

When("I sign up with that email address") do
  @response = MyApp::API.post("/signup", {
    email: @email_addr,
    password: "SecureP@ss1",
    name: "Test User"
  })
end

Then("I should receive a verification email") do
  @email = wait_for_email_at(@address["id"], timeout: 30)
  expect(@email).not_to be_nil
end

Then("the email subject should contain {string}") do |text|
  expect(@email["subject"]).to include(text)
end

Then("the email should contain a verification link") do
  body = @email["text_body"] || @email["html_body"]
  @verification_link = body[%r{https?://\S+/verify\S*}]
  expect(@verification_link).not_to be_nil
end

Then("I should see an error message {string}") do |message|
  expect(@response.body).to include(message)
end

Running Cucumber

bundle install
export ONESHOT_API_KEY="osm_live_your_key"
bundle exec cucumber

RSpec integration

spec/spec_helper.rb

require "oneshot"

RSpec.configure do |config|
  config.before(:suite) do
    @oneshot_client = OneShot::Client.new(api_key: ENV.fetch("ONESHOT_API_KEY"))
    @run_label = "rspec-#{SecureRandom.hex(4)}"
  end

  config.after(:suite) do
    @oneshot_client&.delete_by_label(@run_label) rescue nil
  end
end

# Shared context for email tests
RSpec.shared_context "with temporary email" do
  let(:oneshot_client) { OneShot::Client.new(api_key: ENV.fetch("ONESHOT_API_KEY")) }
  let(:run_label) { "rspec-#{SecureRandom.hex(4)}" }
  let(:email_address) do
    oneshot_client.create(ttl: 300, label: run_label)
  end

  after do
    oneshot_client.delete(email_address["id"]) rescue nil
  end
end

spec/signup_spec.rb

require "spec_helper"

RSpec.describe "User signup" do
  include_context "with temporary email"

  it "sends a verification email on signup" do
    # Trigger signup
    MyApp::API.post("/signup", {
      email: email_address["address"],
      password: "SecureP@ss1"
    })

    # Wait for the email
    email = oneshot_client.wait_for_email(email_address["id"], timeout: 30)

    expect(email["subject"]).to include("Verify your account")
    expect(email["text_body"]).to include("Click")
  end

  it "includes the user's name in the verification email" do
    MyApp::API.post("/signup", {
      email: email_address["address"],
      password: "SecureP@ss1",
      name: "Alice"
    })

    email = oneshot_client.wait_for_email(email_address["id"], timeout: 30)
    expect(email["text_body"]).to include("Alice")
  end
end

Running RSpec

bundle install
export ONESHOT_API_KEY="osm_live_your_key"
bundle exec rspec