Ruby SDK
The HostRepute Ruby gem, hostrepute version 0.1.0, wraps the REST API v1 with immutable models, automatic retries and
helpers that start a check and wait for its result. It needs Ruby 3.2 or later and uses only the standard library
(net/http, json, openssl).
Install
Section titled “Install”gem "hostrepute", "~> 0.1"Then run bundle install, or install it without Bundler with gem install hostrepute.
Create a client
Section titled “Create a client”Hostrepute::Client.new takes a team API token and keyword options. Create the token in
Settings, then API tokens with the abilities your code needs;
Authentication and API tokens explains them.
require "hostrepute"
client = Hostrepute::Client.new( ENV.fetch("HOSTREPUTE_API_TOKEN"), timeout: 45, user_agent: "deploy-checks/1.4")
client.hosts.iterate(per_page: 100).each do |host| puts "#{host.host} #{host.blocklisted? ? "listed" : "not listed"}"endTo share one client across your app, configure it once and use Hostrepute.client. The configuration reads the token
from HOSTREPUTE_API_TOKEN unless you call with_token:
Hostrepute.configure do |config| config.with_timeout(45)endHostrepute.client without configure also reads HOSTREPUTE_API_TOKEN, and raises an ArgumentError when it’s
empty. Client.new raises an ArgumentError for an empty token, an invalid base URL or retry setting, or a protected
default header. It ignores options it doesn’t know, so check their spelling.
| Option | Default | What it does |
|---|---|---|
base_url: |
https://app.hostrepute.com |
The API’s address. Give the origin only: the SDK adds /api/v1/… to every path. |
timeout: |
30 seconds |
The read and write timeout of each request. |
open_timeout: |
10 seconds |
The connection timeout. |
user_agent: |
hostrepute-ruby/0.1.0 (ruby <version>) |
The User-Agent header. |
retry: |
{max_attempts: 3, base_delay_ms: 200, max_delay_ms: 5000} |
The retry policy. See retries. |
default_headers: |
{} |
Extra headers on every request. Authorization, Accept, Content-Type and User-Agent aren’t allowed. |
logger: |
none | A Logger. The SDK logs each request and response at debug level (method, URL, attempt, status), never the token. |
The builder (Hostrepute::Client.builder, also used by configure) has a with_… method for each option.
Resources
Section titled “Resources”The client has one method per API resource. The names follow the API, not the portal:
| In HostRepute | Method | Methods | API |
|---|---|---|---|
| Domains and IPs | client.hosts |
list, iterate, get, create, update, delete |
/api/v1/hosts |
| Blocklists | client.data_sources |
list, iterate, get |
/api/v1/data-sources |
| Check profiles | client.monitoring_profiles |
list, iterate, get, create, update, delete |
/api/v1/data-source-groups |
| Alert channels | client.alerts |
list, iterate, get, create, update, delete |
/api/v1/alerts |
| Delivery hours | client.contact_groups |
list, iterate, get, create, update, delete |
/api/v1/alert-groups |
| Blocklist checks | client.blacklist |
check, status, check_and_wait, wait_for |
/api/v1/blacklist |
| Certificate checks | client.certificates |
check, status, check_and_wait, wait_for |
/api/v1/certificates |
| Email authentication checks | client.deliverability |
check, status, check_and_wait, wait_for |
/api/v1/deliverability |
| Prometheus metrics | client.metrics |
get |
/api/v1/metrics |
list(page:, per_page:)returns aHostrepute::Pagewithdata,current_page,last_page,per_page,totalandhas_next_page?.per_pageis 1–100; the API sends 15 when you leave it out.iterate(per_page:)returns a lazy enumerator over every page. Read Pagination.get,updateanddeletetake theuuid, exceptdata_sources.get, which takes the blocklist’s numericid.updatesendsPUTwith the keywords you pass;nilmeans “leave it out”.- The SDK has no method for sending a test alert. Call
POST /api/v1/alerts/{alert}/testdirectly.
Add a domain or IP
Section titled “Add a domain or IP”Every domain or IP needs a check profile. The SDK calls it
monitoring_profile_uuid: and sends it as data_source_group_uuid:
profile = client.monitoring_profiles.list.data.find(&:is_default)
host = client.hosts.create( name: "Mail", host: "mail.example.com", monitoring_profile_uuid: profile.uuid)Set delivery hours and an alert channel
Section titled “Set delivery hours and an alert channel”Delivery hours are contact_groups in the SDK. schedule: is a Hash that the
SDK sends unchanged, so give it the shape the API expects:
- Keys
mon,tue,wed,thu,fri,satandsun, each withenabled,startandend. Times areHH:MM, and an enabled day must end after it starts. Symbol or string keys both work. - A day you leave out gets no alerts. Leave
schedule:out entirely for delivery hours that are open at any time. updateleaves outnilkeywords, so it can’t turn existing delivery hours back to any time. Send"schedule": nullwith the REST API instead.
weekday = {enabled: true, start: "08:00", end: "18:00"}weekend = {enabled: false, start: "08:00", end: "18:00"}
hours = client.contact_groups.create( name: "Business hours", timezone: "America/New_York", schedule: { mon: weekday, tue: weekday, wed: weekday, thu: weekday, fri: weekday, sat: weekend, sun: weekend })An alert channel is alerts. settings: holds the field the channel type needs
(email here; Set up alert channels lists them all), and contact_group_uuids: the
delivery hours it follows:
channel = client.alerts.create( type: Hostrepute::AlertType::EMAIL, frequency: Hostrepute::AlertFrequency::ONCE_ON_LISTED_DELISTED, contact_group_uuids: [hours.uuid])For frequency:, use AlertFrequency::ONCE_ON_LISTED_DELISTED
(Listed and removed in the portal) or AlertFrequency::ONCE_PER_CHECK_WHILE_LISTED
(Only when listed). The SDK also defines AlertFrequency::DAILY, which the API
rejects with 422, and AlertFrequency::EVERY_CHECK, a retired value that the API stores as
once_on_listed_delisted.
Run checks and wait for the result
Section titled “Run checks and wait for the result”Each check uses 1 credit (credit costs). Blocklist and certificate checks run in the background:
checkstarts the check and returns it withstatus"pending".status(uuid)reads it again.wait_for(uuid)pollsstatusuntil the check iscompletedorfailed.check_and_waitischeckfollowed bywait_for.
timeout: sets how long to wait (60 seconds by default) and poll_interval: how often to poll (2 seconds). When the
time runs out, the SDK raises Hostrepute::TimeoutError with the check’s scan_uuid and the last status it read in
last_observation:
begin scan = client.blacklist.check_and_wait(host: "mail.example.com", timeout: 120, poll_interval: 5) puts "#{scan.status}: listed on #{scan.listed_data_source_count} of #{scan.data_source_count} blocklists"rescue Hostrepute::TimeoutError => e puts "Still running: #{e.scan_uuid} (#{e.last_observation.status}). Resume with client.blacklist.wait_for."endA failed check comes back as a result, not an exception: check status and error_message. The full per-blocklist
result is in scan.result. A certificate check works the same way:
cert = client.certificates.check_and_wait( host: "smtp.example.com", protocol: Hostrepute::CertificateProtocol::STARTTLS_SMTP, port: 587)To be told when a check ends instead of polling, pass callback_url: to blacklist.check or certificates.check. A
callback’s signature can’t be verified yet, so treat the callback as a notification and read the result with
status, as Receive webhooks explains. The SDK’s own webhook helpers don’t
match what HostRepute sends, so don’t use them.
Run checks and wait for results covers the API side of polling and callbacks.
Email authentication checks
Section titled “Email authentication checks”An email authentication check answers in the same request, so deliverability.check already returns the final
result. Its status is passed, warning or failed, or skipped when none of the checks applied to the host:
result = client.deliverability.check( host: "example.com", checks: [Hostrepute::DeliverabilityCheck::SPF_RECORD, Hostrepute::DeliverabilityCheck::DMARC_RECORD])puts result.statusRetries
Section titled “Retries”The SDK retries a failed request up to 3 attempts in total:
429is retried for every method. The SDK waits forRetry-After(seconds or an HTTP date) when the response has it. A429means the API refused the request before running it, so a retried check isn’t charged twice.5xxand network errors are retried only forGET,HEAD,OPTIONS,PUTandDELETE. APOSTthat creates something or starts a check isn’t retried, so the SDK never starts a check twice.- Backoff without
Retry-Afteris a random delay between zero and 200 ms × 2attempt − 1, capped at 5 seconds. - When the attempts run out, you get the exception for the last response, for example a
RateLimitError.
tuned = Hostrepute::Client.new( ENV.fetch("HOSTREPUTE_API_TOKEN"), retry: {max_attempts: 5, base_delay_ms: 500, max_delay_ms: 10_000})retry: {max_attempts: 1} turns retries off. Rate limits lists the limits behind 429.
Errors
Section titled “Errors”All SDK exceptions inherit from Hostrepute::Error. API errors inherit from Hostrepute::ApiError, with status,
message, errors (field names to messages), body, raw_body, headers and request_id.
| Status or cause | Exception | Extra attributes |
|---|---|---|
401 |
AuthenticationError |
|
402 |
PaymentRequiredError |
required_credits, available_credits |
403 |
AuthorizationError |
|
404 |
NotFoundError |
|
422 |
ValidationError |
errors |
429 |
RateLimitError |
retry_after, in seconds |
5xx |
ServerError |
|
| Any other status | ApiError |
|
| No response, or an HTTP timeout | NetworkError |
cause, the original exception |
| A wait helper’s timeout | TimeoutError |
scan_uuid, last_observation |
| A response the SDK can’t read | DecodingError |
raw_body |
begin client.blacklist.check(host: "mail.example.com")rescue Hostrepute::PaymentRequiredError => e puts "Needs #{e.required_credits} credits, #{e.available_credits} left"rescue Hostrepute::ValidationError => e e.errors.each { |field, messages| puts "#{field}: #{messages.join(", ")}" }rescue Hostrepute::RateLimitError => e puts "Rate limited, retry in #{e.retry_after} s"endErrors shows the response body behind each status.
Prometheus metrics
Section titled “Prometheus metrics”client.metrics.get returns the team’s metrics; body (or to_s) is the Prometheus text. The token needs
metrics:read.
puts client.metrics.get.bodybody has binary encoding; call force_encoding("UTF-8") on it before you work with it as text.
Related
Section titled “Related”- Official SDKs compares the five SDKs.
- Make your first API call uses the REST API without an SDK.
- Run checks and wait for results explains polling, callbacks and credits.
- API token abilities lists what each ability allows.
- The API reference documents every operation.

