unitpost

Official Unitpost SDK for Ruby. Send email and manage contacts, segments, campaigns, templates, domains, suppressions, and webhooks.

Install

gem install unitpost

Or add to your Gemfile:

gem "unitpost"

Requires Ruby 3.0+.

Quick start

require "unitpost"

unitpost = Unitpost::Client.new # reads UNITPOST_API_KEY (or pass api_key:)

result = unitpost.emails.send({
  from: "Acme <you@yourdomain.com>", # bare address works too
  to: "customer@example.com",
  subject: "Hello",
  html: "<p>It works!</p>"
})

if result.error
  warn "#{result.error.code}: #{result.error.message}"
else
  puts result.data["id"]
end

Every method returns a Unitpost::Result with #data and #error (and a #success? helper). An API-level failure populates #error (a Unitpost::Error) instead of raising.

Authentication

Pass api_key: to Unitpost::Client.new, or set UNITPOST_API_KEY. Create a key at unitpost.com → Settings → API keys.

Client surface

Accessor Covers
emails Send, batch, list, get, update, stats, inbound (received_*)
contacts CRUD, import jobs
contact_fields Custom field definitions
segments Segments + membership
topics Topics + contact topic prefs
campaigns Draft, schedule, send, pause/resume
templates Template CRUD
brand_kits Brand kit
domains Domains + verify
webhooks Endpoints + verify
api_keys API key management
suppressions Suppression list
usage Billing-period usage snapshot

Pagination

unitpost.contacts.list_all.each do |contact|
  puts contact["email"]
end

Suppressions

The suppression list holds addresses excluded from every send — single, batch, and campaign, including cc/bcc — case-insensitively. Hard bounces and spam complaints are added automatically; you can also add your own:

# Suppress one address, or many in a single call (idempotent — re-adding is a no-op).
unitpost.suppressions.create({ email: "bounced@example.com", reason: "manual" })
unitpost.suppressions.create({ emails: ["a@example.com", "b@example.com"] })

# Look one up by id or email; list (optionally filter scope); un-suppress.
unitpost.suppressions.get("bounced@example.com")
unitpost.suppressions.list_all.each { |s| puts "#{s["email"]} #{s["reason"]}" }
unitpost.suppressions.delete("bounced@example.com")

reason accepts manual, import, or api — the engine-written bounce and complaint reasons are read-only. Unitpost-wide (platform) entries are visible but can't be removed via the API.

Verifying webhooks

event = Unitpost.verify_webhook(
  payload: raw_body,            # the raw request body string
  secret: ENV.fetch("UNITPOST_WEBHOOK_SECRET"),
  headers: request.headers
)

Or from the client (Resend-style alias):

event = client.webhooks.verify(payload: raw_body, secret: secret, headers: request.headers)

Idempotency

emails.send and emails.batch are safe to retry by default — when you don't pass idempotency_key:, the SDK generates one so an automatic retry can't double-send. Pass your own to dedupe across separate calls:

unitpost.emails.send({ ... }, idempotency_key: SecureRandom.uuid)
unitpost.emails.batch([ ... ], idempotency_key: SecureRandom.uuid)

The server dedupes on (workspace, idempotency key) for 24h and replays the original result instead of re-sending (the email id for a send, the full list envelope for a batch).

Retries

Transient failures are retried automatically: 429, 5xx, and transport errors (timeouts / network) retry up to max_retries times (default 2). A 429/503 Retry-After is honored; otherwise exponential backoff with full jitter is used. Client errors (401/403/404/409/422) are never retried. Only requests safe to replay are retried: GETs and writes carrying an idempotency key (which emails.send and emails.batch always do); any other keyless write is never auto-retried.

# Defaults shown; set max_retries: 0 to disable.
unitpost = Unitpost::Client.new(max_retries: 2, retry_base_delay: 0.5, max_retry_delay: 20.0)

Resources

| | | | --- | --- | | Docs + API reference | https://www.unitpost.com/docs | | Product / SMTP / MCP guides | https://www.unitpost.com/guides | | OpenAPI JSON | https://www.unitpost.com/api/v1/openapi.json | | LLM index | https://www.unitpost.com/llms.txt | | Dashboard / API keys | https://www.unitpost.com | | Email components (@unitpost/email) | https://www.unitpost.com/components | | Template gallery | https://www.unitpost.com/templates/gallery | | Playground | https://www.unitpost.com/playground | | Migration Assistant | https://www.unitpost.com/migration | | MCP + Agent Skills | https://www.unitpost.com/guides#ai-mcp · https://www.unitpost.com/guides#ai-skills | | Support | support@unitpost.com |

License

MIT