AxeneAxene Docs
SDKs

Ruby SDK

Install and use the official Axene Mailer Ruby SDK to send email, manage domains, contacts, suppressions, templates, and webhooks.

The Axene Mailer Ruby SDK is a thin, idiomatic wrapper over the Mailer API. It ships as the axene-mailer gem on RubyGems and exposes one client object with a resource accessor per API group.

Source: github.com/Axene-Solutions/axene-sdks/tree/main/packages/ruby


Installation

The gem requires Ruby 3.0 or newer and has zero runtime dependencies (it uses net/http from the standard library).

Install it directly:

gem install axene-mailer

Or add it to your Gemfile:

gem "axene-mailer"

Then run bundle install.


Client setup

Construct a client with your API key. The key starts with axm_k_ and is passed on every request as a bearer token.

require "axene/mailer"
 
client = Axene::Mailer::Client.new(api_key: ENV.fetch("AXENE_API_KEY"))

Never hard-code your API key. Load it from an environment variable or a secrets manager.

The constructor accepts these options:

OptionTypeDefaultDescription
api_keyString(required)Your API key. Must start with axm_k_.
base_urlStringhttps://mail.axene.ioAPI base URL. Override for a self-hosted or staging environment.
max_retriesInteger3Retry attempts for 429 and 5xx responses, with exponential backoff that honours Retry-After.
timeoutNumeric30Per-request open and read timeout, in seconds.
client = Axene::Mailer::Client.new(
  api_key: ENV.fetch("AXENE_API_KEY"),
  base_url: "https://mail.axene.io",
  max_retries: 5,
  timeout: 60
)

The client exposes six resources: client.emails, client.domains, client.contacts, client.suppressions, client.templates, and client.webhooks.


Send an email

client.emails.send accepts keyword arguments (or a Hash). You pass a clean from, which the SDK maps to the API's from_ wire field. Anywhere an address is expected you may pass a bare string (treated as { email: ... }) or a Hash with email and an optional name.

response = client.emails.send(
  from: { email: "[email protected]", name: "Your Company" },
  to: "[email protected]",
  subject: "Your receipt",
  html: "<h1>Thanks for your order</h1><p>Your receipt is attached.</p>",
  text: "Thanks for your order. Your receipt is attached."
)
 
puts response[:id]      # "a1b2c3d4-5678-90ab-cdef-1234567890ab"
puts response[:status]  # "queued"

Responses are plain Ruby Hashes with symbol keys. A send returns:

{
  id: "a1b2c3d4-5678-90ab-cdef-1234567890ab",
  status: "queued",
  message_id: "<[email protected]>",
  rejection_reason: nil
}

to, cc, and bcc each accept a single address or an array of addresses. Additional supported keys include reply_to, headers, tags, send_at (a Time or ISO 8601 string, for scheduled delivery), and attachments.

client.emails.send(
  from: "[email protected]",
  to: [{ email: "[email protected]", name: "Jane Doe" }, "[email protected]"],
  cc: "[email protected]",
  subject: "Weekly report",
  html: "<p>See attached.</p>",
  tags: ["reports", "weekly"],
  reply_to: "[email protected]",
  send_at: Time.now + 3600,
  attachments: [
    {
      filename: "report.pdf",
      content_base64: Base64.strict_encode64(File.binread("report.pdf")),
      content_type: "application/pdf"
    }
  ]
)

Emails

# Send a batch (one call, returns a per-message result set)
client.emails.send_batch([
  { from: "[email protected]", to: "[email protected]", subject: "Hi", html: "<p>1</p>" },
  { from: "[email protected]", to: "[email protected]", subject: "Hi", html: "<p>2</p>" }
])
 
# Dry-run validation (never sends)
result = client.emails.validate(
  from: "[email protected]",
  to: "[email protected]",
  subject: "Test",
  html: "<p>Test</p>"
)
result[:can_send]  # => true
 
# List, get, events
client.emails.list(status: "delivered", page: 0, limit: 20)
client.emails.get("a1b2c3d4-...")
client.emails.events("a1b2c3d4-...")
 
# Retry a bounced/rejected/failed email
client.emails.retry("a1b2c3d4-...")
 
# Search (q supports to:, from:, status:, domain:, tag: tokens)
client.emails.search(q: "welcome status:delivered", page: 0, limit: 20)
 
# Scheduled emails
client.emails.list_scheduled
client.emails.cancel_scheduled("b2c3d4e5-...")
client.emails.send_scheduled_now("b2c3d4e5-...")
 
# Poll for status changes since a Time or ISO 8601 string (max 50 rows)
client.emails.updates(Time.now - 300)
 
# Saved searches
client.emails.get_saved_searches
client.emails.set_saved_searches([{ name: "Bounces", query: "status:bounced" }])

Domains

client.domains.list
client.domains.create("yourdomain.com")          # returns DNS records to publish
client.domains.get("dom_id")
client.domains.delete("dom_id")
client.domains.verify("dom_id")                   # re-check DNS and verify
 
# Health and diagnostics
client.domains.health("dom_id")
client.domains.diagnose("dom_id")
client.domains.mx_status("dom_id")
client.domains.published_records("dom_id")
 
# DKIM rotation and transfer
client.domains.rotate_dkim("dom_id")
client.domains.transfer("dom_id", target_email: "[email protected]", note: "Handover")
 
# Availability checks
client.domains.check_availability("yourdomain.com")
client.domains.check("yourdomain.com")

Contacts

# Lists
client.contacts.list_lists
client.contacts.create_list(name: "Newsletter", description: "Monthly updates")
client.contacts.get_list("list_id", page: 0, limit: 50)
client.contacts.update_list("list_id", name: "Renamed")
client.contacts.delete_list("list_id")
 
# Contacts within a list
client.contacts.add_contact("list_id", email: "[email protected]", name: "Jane", metadata: { plan: "pro" })
client.contacts.remove_contact("list_id", "contact_id")
 
# CSV import (file may be raw bytes or a path; header row required)
client.contacts.upload_csv("list_id", "contacts.csv")
 
# Templated bulk send to every contact in the list
client.contacts.bulk_send(
  "list_id",
  sender_address_id: "sender_id",
  subject: "Hello {{name}}",
  html: "<p>Hi {{name}}, welcome aboard.</p>"
)

Bulk send subject and bodies support {{email}}, {{name}}, and {{metadata_key}} placeholders.


Suppressions

The suppression list endpoints return a paginated envelope ({ items:, total:, page:, limit: }). The email argument maps to the API's email_address wire field.

page = client.suppressions.list(page: 0, limit: 50, search: "example.com")
page[:items]
 
client.suppressions.add(email: "[email protected]", reason: "manual")
 
# Bulk import (one email per line; raw bytes or a path)
client.suppressions.bulk_upload("suppressions.txt")
 
client.suppressions.remove("suppression_id")

Templates

Reusable templates are available on the Starter plan and above. For this resource only, html maps to html_body and text maps to text_body.

client.templates.list
client.templates.create(name: "Welcome", subject: "Welcome {{name}}", html: "<p>Hi {{name}}</p>")
client.templates.get("tpl_id")
client.templates.update("tpl_id", subject: "Welcome aboard {{name}}")
client.templates.delete("tpl_id")
client.templates.duplicate("tpl_id")

The variables field is derived server-side from {{name}} placeholders, so you do not pass it.


Webhooks

The deliveries listing returns a paginated envelope ({ items:, total:, page:, limit: }).

client.webhooks.list
client.webhooks.create(url: "https://example.com/hooks", events: ["email.delivered"])
client.webhooks.update("wh_id", events: ["email.delivered", "email.bounced"], is_active: true)
client.webhooks.delete("wh_id")
 
# Queue a sample email.delivered delivery to test the endpoint
client.webhooks.test("wh_id")
 
# Inspect deliveries
client.webhooks.list_deliveries("wh_id", page: 0, limit: 20, status: "failed")
client.webhooks.get_delivery("wh_id", "delivery_id")

Error handling

Any non-2xx response (and any transport failure that survives every retry) raises Axene::Mailer::Error. Inspect #status, #code, and #message to branch on specific failures. A #status of 0 indicates a network or transport failure with no HTTP response, and #detail holds the raw parsed response body for debugging.

begin
  client.emails.send(
    from: "[email protected]",
    to: "[email protected]",
    subject: "Your receipt",
    html: "<p>Thanks.</p>"
  )
rescue Axene::Mailer::Error => e
  warn "Send failed (#{e.status}): #{e.message}"
  warn "Code: #{e.code}" if e.code
end
ReaderTypeDescription
#statusIntegerHTTP status code. 0 for transport or network failures.
#codeString, nilMachine-readable error code from the API body, when present.
#messageStringHuman-readable error message.
#detailObject, nilThe raw parsed response body.

The SDK retries 429 and 5xx responses automatically (up to max_retries), honouring Retry-After. Client errors in the 4xx range are never retried and surface immediately.


Configuration

All configuration is passed to the constructor. There is no global or file-based config.

client = Axene::Mailer::Client.new(
  api_key: ENV.fetch("AXENE_API_KEY"),  # required, starts with axm_k_
  base_url: ENV.fetch("AXENE_BASE_URL", "https://mail.axene.io"),
  max_retries: 3,
  timeout: 30
)
  • base_url is useful for pointing the client at a staging environment.
  • max_retries and timeout tune the network behaviour for your workload.
  • Multipart uploads (upload_csv, bulk_upload) are not retried, because uploads are not idempotent.

Next steps

On this page