Skip to content

Go SDK

Reference
For:
Developers

The HostRepute Go client, version 0.1.0, wraps the REST API v1 with typed request and response structs, a context.Context on every call, automatic retries and helpers that start a check and wait for its result. It uses only the Go standard library and needs Go 1.23 or later.

Install (when it's published)
go get github.com/hostrepute/hostrepute-go

hostrepute.New takes a team API token and any number of options. Create the token in Settings, then API tokens with the abilities your program needs; Authentication and API tokens explains them. The SDK doesn’t read environment variables itself: the examples on this page read the token from HOSTREPUTE_API_TOKEN.

main.go
package main
import (
"context"
"fmt"
"log"
"os"
"time"
hostrepute "github.com/hostrepute/hostrepute-go"
)
func main() {
client, err := hostrepute.New(os.Getenv("HOSTREPUTE_API_TOKEN"),
hostrepute.WithTimeout(45*time.Second),
hostrepute.WithUserAgent("deploy-checks/1.4"),
)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
for host, err := range client.Hosts.Iterate(ctx, &hostrepute.HostListParams{PerPage: hostrepute.Int(100)}) {
if err != nil {
log.Fatal(err)
}
fmt.Println(host.Host, host.IsBlocklisted())
}
}

New returns an error for an empty token, a base URL without a scheme or host, a timeout of zero or less, or an invalid retry setting.

Option Default What it does
WithBaseURL(url) https://app.hostrepute.com The API’s address. Give the origin only: the SDK adds /api/v1/… to every path.
WithTimeout(d) 30 seconds The time limit for each HTTP request. Ignored when you pass WithHTTPClient.
WithHTTPClient(c) &http.Client{Timeout: 30 * time.Second} Your own *http.Client, for proxies, custom transports or instrumentation. The SDK uses it as is.
WithUserAgent(s) hostrepute-go/0.1.0 (go) The User-Agent header.
WithRetry(cfg) 3 attempts, 200 ms base delay, 5 s maximum The retry policy. See retries.
WithDefaultHeader(name, value) none An extra header on every request. It can’t replace Authorization, Accept, Content-Type or User-Agent.
WithLogger(l) none Any logger with Debug, Info, Warn and Error methods, such as *slog.Logger. The SDK logs each request and response at debug level, never the token.

The client has one field per API resource. The names follow the API, not the portal:

In HostRepute Field Methods API
Domains and IPs client.Hosts List, Iterate, Get, Create, Update, Delete /api/v1/hosts
Blocklists client.DataSources List, Iterate, Get /api/v1/data-sources
Check profiles client.MonitoringProfiles 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.ContactGroups List, Iterate, Get, Create, Update, Delete /api/v1/alert-groups
Blocklist checks client.Blacklist Check, Status, CheckAndWait, WaitFor /api/v1/blacklist
Certificate checks client.Certificates Check, Status, CheckAndWait, WaitFor /api/v1/certificates
Email authentication checks client.Deliverability Check, Status, CheckAndWait, WaitFor /api/v1/deliverability
Prometheus metrics client.Metrics Get /api/v1/metrics
  • List returns one page as a *Page[T] with Data, Meta, Links and HasNext(). Set Page and PerPage (1–100; the API sends 15 when you leave it out) in the resource’s list params. Iterate walks every page as an iter.Seq2[T, error] for range. Read Pagination.
  • Get, Update and Delete take the uuid, except DataSources.Get, which takes the blocklist’s numeric id.
  • Update sends PUT with the fields you set. Update params are pointers: use hostrepute.String, hostrepute.Int and hostrepute.Bool to fill them.
  • The SDK has no method for sending a test alert. Call POST /api/v1/alerts/{alert}/test directly.

Every domain or IP needs a check profile. The SDK calls it MonitoringProfileUUID and sends it as data_source_group_uuid:

Add a domain with the default check profile
profiles, err := client.MonitoringProfiles.List(ctx, nil)
if err != nil {
log.Fatal(err)
}
var profileUUID string
for _, p := range profiles.Data {
if p.IsDefault {
profileUUID = p.UUID
}
}
host, err := client.Hosts.Create(ctx, hostrepute.HostCreateParams{
Name: "Mail",
Host: "mail.example.com",
MonitoringProfileUUID: profileUUID,
})

Delivery hours are ContactGroups in the SDK. Schedule is a map[string]any that the SDK sends unchanged, so give it the shape the API expects:

  • Keys mon, tue, wed, thu, fri, sat and sun, each with enabled, start and end. Times are HH:MM, and an enabled day must end after it starts.
  • A day you leave out gets no alerts. Leave Schedule out entirely for delivery hours that are open at any time.
  • Update leaves out empty values, so it can’t turn existing delivery hours back to any time. Send "schedule": null with the REST API instead.
  • The SDK’s README shows a days and hours shape: the API rejects it with 422.
Weekdays 08:00 to 18:00 in New York
day := func(enabled bool) map[string]any {
return map[string]any{"enabled": enabled, "start": "08:00", "end": "18:00"}
}
hours, err := client.ContactGroups.Create(ctx, hostrepute.ContactGroupCreateParams{
Name: "Business hours",
Timezone: "America/New_York",
Schedule: map[string]any{
"mon": day(true), "tue": day(true), "wed": day(true), "thu": day(true), "fri": day(true),
"sat": day(false), "sun": day(false),
},
})

An alert channel is Alerts. Settings holds the field the channel type needs (email here; Set up alert channels lists them all), and ContactGroupUUIDs the delivery hours it follows:

An email alert channel that follows the delivery hours
channel, err := client.Alerts.Create(ctx, hostrepute.AlertCreateParams{
Type: hostrepute.AlertTypeEmail,
Frequency: hostrepute.AlertFrequencyOnceOnListedDelisted,
Settings: map[string]any{"email": "[email protected]"},
ContactGroupUUIDs: []string{hours.UUID},
})

For Frequency, use AlertFrequencyOnceOnListedDelisted (Listed and removed in the portal) or AlertFrequencyOncePerCheckWhileListed (Only when listed). The SDK also defines AlertFrequencyDaily, which the API rejects with 422, and AlertFrequencyEveryCheck, a retired value that the API stores as once_on_listed_delisted.

Each check uses 1 credit (credit costs). Blocklist and certificate checks run in the background:

  • Check starts the check and returns it with Status pending.
  • Status reads it again by UUID.
  • WaitFor polls Status until the check is completed or failed.
  • CheckAndWait is Check followed by WaitFor.

WaitOptions sets how long to wait (Timeout, 60 seconds by default) and how often to poll (PollInterval, 2 seconds). When the time runs out, the error matches hostrepute.ErrTimeout (test it with hostrepute.IsTimeout) and the returned scan holds the last status read, so you can resume with WaitFor:

Run a blocklist check and wait up to two minutes
scan, err := client.Blacklist.CheckAndWait(ctx,
hostrepute.BlacklistCheckParams{Host: "mail.example.com"},
hostrepute.WaitOptions{Timeout: 2 * time.Minute, PollInterval: 5 * time.Second},
)
if hostrepute.IsTimeout(err) && scan != nil {
log.Printf("scan %s is still running; resume with client.Blacklist.WaitFor", scan.UUID)
return
}
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s: listed on %d of %d blocklists\n", scan.Status, scan.ListedDataSourceCount, scan.DataSourceCount)

A failed check comes back as a result, not an error: check Status and ErrorMessage. The full per-blocklist result is in scan.Result. A certificate check works the same way; Protocol takes a pointer:

Check a mail server's certificate over STARTTLS
protocol := hostrepute.CertificateProtocolStartTLSSMTP
cert, err := client.Certificates.CheckAndWait(ctx,
hostrepute.CertificateCheckParams{Host: "smtp.example.com", Protocol: &protocol, Port: hostrepute.Int(587)},
hostrepute.WaitOptions{},
)

To be told when a check ends instead of polling, set CallbackURL on BlacklistCheckParams or CertificateCheckParams. 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.

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:

Check SPF and DMARC for a domain
result, err := client.Deliverability.Check(ctx, hostrepute.DeliverabilityCheckParams{
Host: "example.com",
Checks: []hostrepute.DeliverabilityCheck{
hostrepute.DeliverabilityCheckSPFRecord,
hostrepute.DeliverabilityCheckDMARCRecord,
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Status)

The SDK retries a failed request up to 3 attempts in total:

  • 429 is retried for every method. The SDK waits for Retry-After (seconds or an HTTP date) when the response has it. A 429 means the API refused the request before running it, so a retried check isn’t charged twice.
  • 5xx and network errors are retried only for GET, HEAD, OPTIONS, PUT and DELETE. A POST that creates something or starts a check isn’t retried, so the SDK never starts a check twice.
  • Backoff without Retry-After is a random delay between zero and 200 ms × 2attempt − 1, capped at 5 seconds.
  • When the attempts run out, you get the error for the last response, for example a *RateLimitError. Cancelling the context stops the wait at once.
Five attempts with longer waits
client, err := hostrepute.New(token,
hostrepute.WithRetry(hostrepute.RetryConfig{
MaxAttempts: 5,
BaseDelay: 500 * time.Millisecond,
MaxDelay: 10 * time.Second,
}),
)

hostrepute.RetryConfig{MaxAttempts: 1} turns retries off. Rate limits lists the limits behind 429.

Every API error is a typed error; use errors.As to tell them apart. Each one embeds *hostrepute.APIError, with StatusCode, Message, Errors, Body, RawBody and Header.

Status or cause Type Extra fields
401 *AuthenticationError
402 *PaymentRequiredError RequiredCredits, AvailableCredits
403 *AuthorizationError
404 *NotFoundError
422 *ValidationError Errors, a map of field names to messages
429 *RateLimitError RetryAfter, a time.Duration
5xx *ServerError
Any other status *APIError
No response *NetworkError Err, the transport error
A response the SDK can’t read *DecodingError RawBody
A wait helper’s timeout matches hostrepute.ErrTimeout
Handle the errors a check can return
_, err := client.Blacklist.Check(ctx, hostrepute.BlacklistCheckParams{Host: "mail.example.com"})
var credits *hostrepute.PaymentRequiredError
var invalid *hostrepute.ValidationError
var limited *hostrepute.RateLimitError
switch {
case errors.As(err, &credits):
log.Printf("needs %d credits, %d left", credits.RequiredCredits, credits.AvailableCredits)
case errors.As(err, &invalid):
for field, messages := range invalid.Errors {
log.Println(field, messages)
}
case errors.As(err, &limited):
log.Printf("rate limited, retry in %s", limited.RetryAfter)
case err != nil:
log.Fatal(err)
}

Errors shows the response body behind each status.

client.Metrics.Get returns the team’s metrics as Prometheus text. The token needs metrics:read.

Print the team's metrics
text, err := client.Metrics.Get(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Print(text)