Skip to content

About

Official Helo Ruby SDK

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

85 Commits

Folders and files

Repository files navigation

helo-email-sdk

Helo Email API (https://helohq.com)

Installation

Add the gem to your Gemfile:

gem "helo-email-sdk"

Then run:

bundle install

Configuration

Configure the SDK once, at boot:

require "helo-email-sdk"

Helo.configure do |config|
  config.api_key = ENV.fetch("HELO_API_KEY")
end

Usage

Each API is a class with class-level methods. Responses come back as model objects with typed attribute readers.

result = Helo::Channels.list

Methods that take parameters accept a hash:

result = Helo::Channels.list(limit: 10)

See the API docs for every method, with a runnable example each.

Errors

Failed requests raise Helo::APIError:

begin
  Helo::Channels.list
rescue Helo::APIError => e
  e.code    # HTTP status
  e.detail  # human-readable detail from the API
  e.errors  # field-level validation errors, when present
end

Rails (ActionMailer)

The gem registers a :helo delivery method with ActionMailer, so existing mailers send through the API without any changes. Configure the SDK in an initializer:

# config/initializers/helo.rb
Helo.configure do |config|
  config.api_key = Rails.application.credentials.helo_api_key
end

and switch the delivery method:

# config/environments/production.rb
config.action_mailer.delivery_method = :helo

Plain ActionMailer fields map to the API: from, to, cc, bcc, reply_to, subject, the text and HTML parts, attachments (attachments.inline ones keep their content id, so image_tag attachments["logo.png"].url works), and any custom headers. API-specific options go in delivery_method_options, which other delivery methods (:test, letter_opener, SMTP) ignore:

class UserMailer < ApplicationMailer
  def welcome(user)
    headers["X-Custom-Header"] = "value" # custom headers are sent as-is
    mail(
      to: user.email,
      subject: "Welcome!",
      delivery_method_options: {
        tags: ["welcome"],
        metadata: { user_id: user.id.to_s },
        tracking: { opens: true, links: false },
        idempotency_key: "welcome-#{user.id}", # retries won't send twice
        channel_id: "..."
      }
    )
  end
end

After delivery, message[:helo_message_id].value is the id the API assigned the message, and failed requests raise Helo::APIError (subject to config.action_mailer.raise_delivery_errors).

Webhook signature verification

Webhook deliveries are signed with the endpoint's signing key. Verify every delivery before acting on it, against the raw request body — parsing and re-serializing the JSON changes the bytes and the signature will not match.

class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def create
    Helo::WebhookSignatures.verify!(
      request.headers["X-Helo-Webhook-Signature"],
      request.raw_post, # raw body, exactly as received
      ENV.fetch("HELO_WEBHOOK_SIGNING_KEY")
    )

    event = JSON.parse(request.raw_post)
    # ... handle the event, then acknowledge quickly
    head :no_content
  rescue Helo::WebhookSignatures::Error
    head :bad_request
  end
end

verify! returns true when the signature is valid and raises otherwise. Each rejection has its own class, so a stale delivery can be treated differently from a genuinely bad one:

Exception Meaning
MalformedHeaderError The header was not in the expected format
UnsupportedVersionError The delivery used a signing scheme this SDK version cannot verify — upgrade the gem
TimestampSkewError Correctly signed, but too old to accept — possible replay, or clock drift
SignatureMismatchError Wrong signing key, or the body was modified in transit

All four inherit from Helo::WebhookSignatures::Error, so rescue that one class to catch any rejection. If you only want a boolean, use valid? instead:

if Helo::WebhookSignatures.valid?(signature_header, raw_body, signing_key)
  # ...
end

The signature header may carry several versions at once (t=...,v1=...,v2=...) while a new signing scheme is being rolled out. This SDK verifies against the newest version it supports (SUPPORTED_VERSIONS) and ignores elements it does not recognize, so a rollout will not break this integration.

To compute a signature yourself — signing a fixture in tests, for example — use Helo::WebhookSignatures.generate(payload, signing_key, timestamp).

APIs

About

Official Helo Ruby SDK

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages