AxeneAxene Docs
SDKs

Go SDK

Official Go SDK for the Axene Mailer API. Send email, manage domains, contacts, suppressions, templates, and webhooks with a typed, idiomatic client.

The Axene Mailer Go SDK is a typed, idiomatic client for the Axene Mailer API. It wraps every Core resource (emails, domains, contacts, suppressions, templates, and webhooks) behind a single Client, handles bearer authentication, JSON encoding, and retries, and maps every API error to a single *Error type.

The module is github.com/Axene-Solutions/axene-mailer-go and lives in its own repository: github.com/Axene-Solutions/axene-mailer-go.

Installation

go get github.com/Axene-Solutions/axene-mailer-go

The SDK requires Go 1.21 or later and depends only on the standard library.

The package name is axene, so you import it directly:

import axene "github.com/Axene-Solutions/axene-mailer-go"

Client setup

Construct a client with axene.New, passing your API key. API keys start with axm_k_ and are passed as a bearer token on every request.

client := axene.New("axm_k_your_api_key")

New accepts variadic axene.Option values to override the defaults:

OptionDefaultDescription
axene.WithBaseURL(url string)https://mail.axene.ioOverride the API base URL.
axene.WithMaxRetries(n int)3Total attempts on 429 and 5xx, including the first.
axene.WithTimeout(d time.Duration)30sPer-request timeout.
axene.WithHTTPClient(hc *http.Client)stdlib clientInject a custom *http.Client for proxies or testing.
client := axene.New(
    "axm_k_your_api_key",
    axene.WithBaseURL("https://mail.axene.io"),
    axene.WithMaxRetries(5),
    axene.WithTimeout(60*time.Second),
)

The resources are reached as fields on the client: client.Emails, client.Domains, client.Contacts, client.Suppressions, client.Templates, and client.Webhooks. The client is safe for concurrent use.

Every method takes a context.Context as its first argument, so you can carry deadlines and cancellation through to the underlying HTTP request.

Send an email

The example below is a complete, runnable program. It builds a SendEmail, sends it, and prints the queued message id.

package main
 
import (
	"context"
	"fmt"
	"log"
 
	axene "github.com/Axene-Solutions/axene-mailer-go"
)
 
func main() {
	client := axene.New("axm_k_your_api_key")
 
	result, err := client.Emails.Send(context.Background(), axene.SendEmail{
		From:    axene.Address{Email: "[email protected]", Name: "Your Company"},
		To:      []axene.Address{axene.Addr("[email protected]")},
		Subject: "Welcome aboard",
		HTML:    "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
		Text:    "Welcome! Thanks for signing up.",
	})
	if err != nil {
		log.Fatalf("send failed: %v", err)
	}
 
	fmt.Printf("queued %s with status %s\n", result.ID, result.Status)
}

The From field is required and must be a registered sender on a verified domain. Provide HTML, Text, or both. axene.Addr("[email protected]") is a helper that builds an Address from a bare email string when you do not need a display name.

Send returns a *SendResult:

FieldTypeDescription
IDstringThe queued email id.
StatusstringQueue status, for example queued.
MessageIDstringThe RFC message id, when available.
RejectionReasonstringSet when the message was rejected.

Emails

ctx := context.Background()
 
// Send a batch (Starter plan and up).
batch, err := client.Emails.SendBatch(ctx, []axene.SendEmail{
	{From: axene.Addr("[email protected]"), To: []axene.Address{axene.Addr("[email protected]")}, Subject: "Hi", Text: "Hello"},
	{From: axene.Addr("[email protected]"), To: []axene.Address{axene.Addr("[email protected]")}, Subject: "Hi", Text: "Hello"},
})
 
// Dry-run a send without delivering it.
validation, err := client.Emails.Validate(ctx, axene.SendEmail{
	From:    axene.Addr("[email protected]"),
	To:      []axene.Address{axene.Addr("[email protected]")},
	Subject: "Test",
	HTML:    "<p>Test</p>",
})
 
// List recent emails, newest first.
emails, err := client.Emails.List(ctx, axene.ListEmailsParams{Status: "delivered", Page: 0, Limit: 20})
 
// Fetch one email with its bodies and event timeline.
detail, err := client.Emails.Get(ctx, "a1b2c3d4-...")
 
// List delivery, open, click, and bounce events.
events, err := client.Emails.Events(ctx, "a1b2c3d4-...")
 
// Re-send a bounced, rejected, or failed email.
retry, err := client.Emails.Retry(ctx, "a1b2c3d4-...")
 
// Search with inline tokens (to:, from:, status:, domain:, tag:).
hits, err := client.Emails.Search(ctx, axene.SearchEmailsParams{Q: "welcome status:delivered"})
 
// Scheduled sends.
scheduled, err := client.Emails.ListScheduled(ctx)
cancelled, err := client.Emails.CancelScheduled(ctx, "b2c3d4e5-...")
sentNow, err := client.Emails.SendScheduledNow(ctx, "b2c3d4e5-...")
 
// Poll for status changes since a timestamp (capped at 50 rows).
updated, err := client.Emails.Updates(ctx, "2026-06-13T10:00:00Z")

To schedule a send for later, set SendAt on the SendEmail to an ISO 8601 timestamp (Starter plan and up).

Pagination is zero-based everywhere: Page: 0 is the first page.

Domains

ctx := context.Background()
 
// List your sending domains.
domains, err := client.Domains.List(ctx)
 
// Register a domain and get the DNS records to publish.
domain, err := client.Domains.Create(ctx, "yourdomain.com")
 
// Fetch, verify, and inspect a domain.
got, err := client.Domains.Get(ctx, "domain-id")
verified, err := client.Domains.Verify(ctx, "domain-id")
health, err := client.Domains.Health(ctx, "domain-id")
diagnosis, err := client.Domains.Diagnose(ctx, "domain-id")
 
// Rotate the DKIM key, returning the new record to publish.
rotation, err := client.Domains.RotateDkim(ctx, "domain-id")
 
// Transfer a domain to another Axene account.
transfer, err := client.Domains.Transfer(ctx, "domain-id", axene.TransferParams{
	TargetEmail: "[email protected]",
	Note:        "Handing over the marketing domain.",
})
 
// Availability and existence checks.
availability, err := client.Domains.CheckAvailability(ctx, "newdomain.com")
check, err := client.Domains.Check(ctx, "yourdomain.com")
 
// Delete a domain.
err = client.Domains.Delete(ctx, "domain-id")

The newly created Domain carries a DnsRecords slice. Publish each record's Host and Value, then call Verify to complete setup.

Contacts

ctx := context.Background()
 
// Manage subscriber lists.
lists, err := client.Contacts.ListLists(ctx)
name := "Newsletter"
list, err := client.Contacts.CreateList(ctx, axene.CreateListParams{Name: name})
detail, err := client.Contacts.GetList(ctx, "list-id", axene.ListContactsParams{Page: 0, Limit: 50})
newName := "Weekly Newsletter"
updated, err := client.Contacts.UpdateList(ctx, "list-id", axene.UpdateListParams{Name: &newName})
err = client.Contacts.DeleteList(ctx, "list-id")
 
// Add and remove contacts.
contact, err := client.Contacts.AddContact(ctx, "list-id", axene.AddContactParams{Email: "[email protected]"})
err = client.Contacts.RemoveContact(ctx, "list-id", "contact-id")
 
// Import contacts from a CSV file (header row required).
csv := []byte("email,name\n[email protected],Jane Doe\n")
imported, err := client.Contacts.UploadCSV(ctx, "list-id", csv, "contacts.csv")
 
// Send a templated email to every contact in a list.
result, err := client.Contacts.BulkSend(ctx, "list-id", axene.BulkSendParams{
	SenderAddressID: "sender-address-id",
	Subject:         "Hello {{name}}",
	HTML:            "<p>Hi {{name}}, thanks for subscribing.</p>",
})

BulkSend injects the list id as contact_list_id automatically, so you leave that field unset. Subject and body may use {{email}}, {{name}}, and {{metadata_key}} placeholders. Optional string fields on the params structs are pointers, so unset fields are omitted from the request.

Suppressions

ctx := context.Background()
 
// List suppressed addresses (paginated envelope).
page, err := client.Suppressions.List(ctx, axene.ListSuppressionsParams{Page: 0, Limit: 50})
for _, s := range page.Items {
	fmt.Println(s.EmailAddress, s.Reason)
}
fmt.Println("total:", page.Total)
 
// Suppress a single address (Reason defaults to "manual").
suppression, err := client.Suppressions.Add(ctx, axene.AddSuppressionParams{Email: "[email protected]"})
 
// Bulk import from a file (one email per line).
file := []byte("[email protected]\n[email protected]\n")
bulk, err := client.Suppressions.BulkUpload(ctx, file, "suppressions.txt")
 
// Remove an address from the suppression list.
err = client.Suppressions.Remove(ctx, "suppression-id")

Suppressions.List returns a *Page[Suppression], the generic envelope used by endpoints that paginate with a total count.

Templates

ctx := context.Background()
 
// Templates are available on the Starter plan and up.
templates, err := client.Templates.List(ctx)
 
subject := "Welcome, {{name}}"
html := "<h1>Hi {{name}}</h1>"
created, err := client.Templates.Create(ctx, axene.CreateTemplateParams{
	Name:    "Welcome email",
	Subject: &subject,
	HTML:    &html,
})
 
got, err := client.Templates.Get(ctx, "template-id")
 
newName := "Welcome email v2"
updated, err := client.Templates.Update(ctx, "template-id", axene.UpdateTemplateParams{Name: &newName})
 
copied, err := client.Templates.Duplicate(ctx, "template-id")
 
err = client.Templates.Delete(ctx, "template-id")

Variables on a Template are derived server-side from {{name}} placeholders and are read-only, so you do not pass them.

Webhooks

ctx := context.Background()
 
// List, create, update, and delete webhooks.
hooks, err := client.Webhooks.List(ctx)
webhook, err := client.Webhooks.Create(ctx, axene.CreateWebhookParams{
	URL:    "https://example.com/hooks/axene",
	Events: []string{"email.delivered", "email.bounced"},
})
active := false
updated, err := client.Webhooks.Update(ctx, "webhook-id", axene.UpdateWebhookParams{IsActive: &active})
err = client.Webhooks.Delete(ctx, "webhook-id")
 
// Send a test delivery to the endpoint.
test, err := client.Webhooks.Test(ctx, "webhook-id")
 
// Inspect delivery attempts (paginated envelope).
deliveries, err := client.Webhooks.ListDeliveries(ctx, "webhook-id", axene.ListDeliveriesParams{Page: 0, Limit: 20})
delivery, err := client.Webhooks.GetDelivery(ctx, "webhook-id", "delivery-id")

The signing Secret is generated by the API and returned in plaintext on every read of a Webhook.

Error handling

Every method returns an error that, on any non-2xx response or a transport failure that survives all retries, is an *axene.Error. Type-assert to inspect it:

result, err := client.Emails.Send(ctx, msg)
if err != nil {
	var apiErr *axene.Error
	if errors.As(err, &apiErr) {
		fmt.Printf("status=%d code=%s message=%s\n", apiErr.Status, apiErr.Code, apiErr.Message)
		switch apiErr.Status {
		case 422:
			// Validation problem, for example an unverified sender.
		case 429:
			// Rate limited.
		}
	}
	return err
}

The Error struct has three fields:

FieldTypeDescription
StatusintThe HTTP status code. 0 means a transport or network failure where no response was received.
CodestringThe machine-readable error code from the API body, when present.
MessagestringA human-readable description of the failure.

Configuration

Retries. The client retries 429 and 5xx responses, as well as transient network failures, up to WithMaxRetries total attempts (default 3). Backoff is exponential starting at 250ms and honors a Retry-After header when the API sends one. Other 4xx responses are never retried. Multipart uploads (UploadCSV, BulkUpload) are not retried because they are not idempotent.

Timeout. WithTimeout sets the per-request timeout (default 30s). For finer control, pass a context.Context with its own deadline; the retry loop respects context cancellation between attempts.

Base URL. WithBaseURL overrides the API host (default https://mail.axene.io). A trailing slash is trimmed for you.

client := axene.New(
	"axm_k_your_api_key",
	axene.WithMaxRetries(5),
	axene.WithTimeout(45*time.Second),
	axene.WithBaseURL("https://mail.axene.io"),
)

Next steps

  • SDKs overview for the full list of official SDKs and shared conventions.
  • Emails API reference for the underlying HTTP endpoints, request fields, and response shapes.

On this page