Send with your stack

Send emails with Go

Send email from Go with the official mailblastr-go module — typed request structs, context variants, and zero third-party dependencies.

The official Go module is the fastest way to send email from Go. It depends only on the standard library, sets the required User-Agent header for you, and retries 429/503 responses automatically.

One thing to get right: the module path ends in /v5. That suffix is mandatory (it is the Go major-version path for the v5 line) — go get without it resolves an old pre-v2 tag. The package name inside is mailblastr, not v5, so import it with an explicit alias.

Prerequisites

  1. A verified domain for your from address. (guide)
  2. An API key (mb_...), read from MAILBLASTR_API_KEY. (Authentication)
  3. Go 1.22 or newer — that is the floor declared in the module go.mod.

Install

go get github.com/shekhu10/mailblastr-sdks/mailblastr-go/v5

Send an email

Construct the client with mailblastr.NewClient and call client.Emails.Send. delivered@mailblastr.dev is the delivery simulator — safe to send to while you wire things up. Do not use example.com: it is a blocked recipient domain and the send comes back 422.

main.go
package main

import (
	"errors"
	"fmt"
	"log"
	"os"

	mailblastr "github.com/shekhu10/mailblastr-sdks/mailblastr-go/v5"
)

func main() {
	client := mailblastr.NewClient(os.Getenv("MAILBLASTR_API_KEY"))

	sent, err := client.Emails.Send(&mailblastr.SendEmailRequest{
		From:    "Acme <hello@yourdomain.com>",
		To:      []string{"delivered@mailblastr.dev"},
		Subject: "Hello from Go",
		Html:    "<p>Sent with the official Go module 🐹</p>",
	})
	if err != nil {
		var apiErr *mailblastr.MailblastrError
		if errors.As(err, &apiErr) {
			log.Fatalf("MailBlastr %d %s: %s", apiErr.StatusCode, apiErr.Name, apiErr.Message)
		}
		log.Fatal(err)
	}

	fmt.Println("Sent email", sent.Id)
}

Handling the response

A successful send returns *mailblastr.CreateEmailResponse, whose only field is Id. Keep it to retrieve the email later or to correlate webhook events.

{
  "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
}

Any non-2xx answer comes back as a *mailblastr.MailblastrError carrying the { statusCode, name, message } envelope. Branch on Name together with StatusCode, never on Message — message text is scrubbed server-side and is not a stable contract. Plan and quota rejections additionally populate Limit, and reputation gates populate Reputation; both are nil on an ordinary error.

var apiErr *mailblastr.MailblastrError
if errors.As(err, &apiErr) {
	switch apiErr.Name {
	case "validation_error":
		// 422 — a bad field, or 403 when the User-Agent header is missing.
	case "daily_quota_exceeded":
		if l := apiErr.Limit; l != nil {
			fmt.Printf("%s cap hit: %d/%d\n", l.Kind, l.Used, l.Limit)
		}
	}
}

Context and tuning

Every method has a context-aware variant with a WithContext suffix, so a send can inherit the deadline of the request that triggered it.

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

sent, err := client.Emails.SendWithContext(ctx, params)

The exported client fields may be overridden before first use. Timeout bounds each attempt (default mailblastr.DefaultTimeout, 30s) and MaxRetries is the retry budget for 429/503 only (default mailblastr.DefaultMaxRetries, 2) — no other status, network error, or timeout is retried, so a send is never silently duplicated.

client := mailblastr.NewClient(os.Getenv("MAILBLASTR_API_KEY"))
client.Timeout = 10 * time.Second
client.MaxRetries = 3
The module always sends a non-empty User-Agent (mailblastr-go/5.1.0), which the API requires on every route — a request without one is rejected with 403 validation_error before it is even authenticated. Setting client.UserAgent = "" falls back to that default rather than producing a client that 403s on every call.
Read the mb_ API key from the environment (or your secrets manager) and keep it server-side. Never compile it into a binary you ship to users — the key can send email as your account.

Next steps