Idiomatic Go email client
The official Mailpipe Go SDK follows idiomatic Go conventions: context-aware requests, typed errors, and a clean interface that composes well with the standard library. It has zero required external dependencies beyond the standard library.
Fetch the module with go get:
go get github.com/mailpipe/mailpipe-go
Then import the package in your Go code:
import "github.com/mailpipe/mailpipe-go"
Requires Go 1.21 or later. The module uses only the Go standard library — no third-party dependencies.
Create a client using functional options:
package main
import (
"os"
mailpipe "github.com/mailpipe/mailpipe-go"
)
func main() {
// Basic client — reads MAILPIPE_API_KEY from environment by default
client := mailpipe.NewClient(os.Getenv("MAILPIPE_API_KEY"))
// Or pass options explicitly
client := mailpipe.NewClient(
os.Getenv("MAILPIPE_API_KEY"),
mailpipe.WithBaseURL("https://api.mailpipe.dev/v1"),
mailpipe.WithTimeout(30 * time.Second),
mailpipe.WithMaxRetries(3),
mailpipe.WithHTTPClient(myCustomHTTPClient),
)
}For long-lived services, create a single client at startup and share it across goroutines. The client is safe for concurrent use:
package app
import (
"os"
mailpipe "github.com/mailpipe/mailpipe-go"
)
// Shared client — initialize once
var Mail = mailpipe.NewClient(os.Getenv("MAILPIPE_API_KEY"))Send a transactional email. All methods accept a context.Context as their first argument:
package main
import (
"context"
"fmt"
"log"
"os"
mailpipe "github.com/mailpipe/mailpipe-go"
)
func main() {
client := mailpipe.NewClient(os.Getenv("MAILPIPE_API_KEY"))
ctx := context.Background()
msg, err := client.Messages.Send(ctx, mailpipe.SendEmailParams{
From: "noreply@yourdomain.com",
To: []string{"user@example.com"},
Subject: "Welcome to our platform!",
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("Sent: %s
", msg.ID)
}Send with CC, BCC, and a named sender:
msg, err := client.Messages.Send(ctx, mailpipe.SendEmailParams{
From: mailpipe.Address{
Email: "invoices@acme.com",
Name: "Acme Billing",
},
To: []string{"customer@example.com"},
CC: []string{"accountant@acme.com"},
BCC: []string{"archive@acme.com"},
ReplyTo: "billing@acme.com",
Subject: "Your invoice #INV-2024-001",
HTML: invoiceHTML,
Text: invoiceText,
Headers: map[string]string{
"X-Invoice-Id": "INV-2024-001",
},
Tags: []string{"invoice", "billing"},
})
if err != nil {
return fmt.Errorf("send invoice: %w", err)
}Retrieve all mailboxes and list messages with filtering:
// List all mailboxes
resp, err := client.Mailboxes.List(ctx, nil)
if err != nil {
return err
}
for _, mb := range resp.Data {
fmt.Printf("%s — %d unread
", mb.Email, mb.UnreadCount)
}
// List unread messages in a specific mailbox
msgs, err := client.Messages.List(ctx, &mailpipe.ListMessagesParams{
MailboxID: "mbx_01hx7k3abc",
Unread: mailpipe.Bool(true),
Limit: 25,
})
if err != nil {
return err
}
fmt.Printf("Found %d unread messages (total: %d)
",
len(msgs.Data), msgs.Meta.Total)
for _, msg := range msgs.Data {
fmt.Printf(" [%s] %s
", msg.ReceivedAt.Format(time.RFC3339), msg.Subject)
}The SDK returns typed errors that implement the standard error interface. Use errors.As to inspect specific error types:
import (
"errors"
mailpipe "github.com/mailpipe/mailpipe-go"
)
_, err := client.Messages.Send(ctx, params)
if err != nil {
var apiErr *mailpipe.APIError
var rateLimitErr *mailpipe.RateLimitError
var authErr *mailpipe.AuthenticationError
switch {
case errors.As(err, &rateLimitErr):
fmt.Printf("Rate limited — retry after %ds
", rateLimitErr.RetryAfter)
case errors.As(err, &authErr):
fmt.Println("Invalid API key — check MAILPIPE_API_KEY")
case errors.As(err, &apiErr):
fmt.Printf("API error %d: %s (request: %s)
",
apiErr.StatusCode, apiErr.Message, apiErr.RequestID)
default:
return fmt.Errorf("unexpected error: %w", err)
}
}All SDK methods respect context cancellation and deadlines, enabling precise timeout and cancellation control across your application:
// Per-request timeout using context
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
msg, err := client.Messages.Send(ctx, params)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
log.Println("Request timed out after 5s")
}
return err
}Propagate cancellation from HTTP request handlers — the SDK will abort the in-flight API call when the client disconnects:
func SendWelcomeHandler(w http.ResponseWriter, r *http.Request) {
// r.Context() is cancelled when the HTTP client disconnects
ctx := r.Context()
_, err := app.Mail.Messages.Send(ctx, mailpipe.SendEmailParams{
From: "welcome@acme.com",
To: []string{r.FormValue("email")},
Subject: "Welcome!",
HTML: welcomeHTML,
})
if err != nil {
if errors.Is(err, context.Canceled) {
// Client disconnected — no need to respond
return
}
http.Error(w, "Failed to send email", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]string{"status": "sent"})
}For background jobs and workers, use a base context with global timeout:
func processEmailQueue(client *mailpipe.Client, queue []SendJob) error {
// Give the entire batch 2 minutes
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
g, ctx := errgroup.WithContext(ctx)
for _, job := range queue {
job := job // capture loop var
g.Go(func() error {
_, err := client.Messages.Send(ctx, job.Params)
return err
})
}
return g.Wait()
}Explore the full REST API documentation with all available endpoints.
View API Reference