sendeez
Zero-dependency Ruby SDK for the Sendeez transactional email API.
require "sendeez"
sendeez = Sendeez::Client.new(ENV.fetch("SENDEEZ_API_KEY"))
email = sendeez.emails.send_email(
{
"from" => { "email" => "hello@example.com", "name" => "Example" },
"to" => [{ "email" => "developer@example.com" }],
"subject" => "Welcome",
"text" => "Your account is ready."
},
idempotency_key: "welcome-user-123" # Optional. The SDK generates one when omitted.
)
puts "#{email['id']} #{email['status']}"
Note the input hash needs explicit { } braces at the call site: emails
methods take keyword arguments too (idempotency_key:, timeout:), and
Ruby 3+ only treats a bare trailing hash as positional when it's wrapped in
braces — otherwise it tries (and fails) to parse it as keywords.
The send method is named send_email, not send — overriding Kernel#send
with a method that also declares keyword parameters hits a real Ruby VM bug
(argument count coming out as 0), not just a style concern.
Errors and agent actions
begin
sendeez.emails.send_email()
rescue Sendeez::Error => e
puts [e.code, e.param, e.request_id, e.action, e.retry_after].inspect
end
The SDK retries GET requests and mutations carrying an idempotency key after
network errors, 429, or 5xx. It never automatically retries an unsafe
mutation.
Every method accepts a timeout: (seconds). This lets applications and AI
agents enforce their own execution deadline:
sendeez.emails.list(timeout: 5)
Testing
Pass transport: to substitute a fake transport, the same role fetch/
opener/transport injection plays in the Node, Python, and PHP SDKs:
fake_transport = lambda do |method, url, headers, body, timeout|
{ status: 200, headers: {}, body: "{}" }
end
client = Sendeez::Client.new("sendeez_example_secret", transport: fake_transport)