Go SDK
The official Go SDK for Veil Mail with idiomatic Go patterns and zero external dependencies.
- Context support on all API methods
- Functional options for client configuration
- Zero external dependencies (stdlib only)
- Typed error handling with helper functions
- Constant-time HMAC webhook signature verification
- Go 1.21+ with generics support
Installation
go get github.com/Resonia-Health/veilmail-goQuick Start
main.go
package main
import (
"context"
"fmt"
"log"
veilmail "github.com/Resonia-Health/veilmail-go"
)
func main() {
client, err := veilmail.New("veil_live_xxxxx")
if err != nil {
log.Fatal(err)
}
email, err := client.Emails.Send(context.Background(), &veilmail.SendEmailParams{
From: "hello@yourdomain.com",
To: []string{"user@example.com"},
Subject: "Hello from Go!",
HTML: "<h1>Welcome!</h1>",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Email sent: %s (status: %s)\n", email.ID, email.Status)
}Configuration
Configure the client with functional options:
config.go
// With custom options
client, err := veilmail.New("veil_live_xxxxx",
veilmail.WithBaseURL("https://custom-api.example.com"),
veilmail.WithTimeout(10 * time.Second),
veilmail.WithHTTPClient(&http.Client{
Transport: &http.Transport{
MaxIdleConns: 10,
},
}),
)Resources
The client exposes the same resources as the Node.js SDK:
| Resource | Description |
|---|---|
client.Emails | Send, batch send, list, get, cancel, update emails |
client.Domains | Create, verify, update, list, delete domains |
client.Templates | Create, update, preview, list, delete templates |
client.Audiences | Manage audiences and subscribers |
client.Campaigns | Create, schedule, send, pause, resume, cancel campaigns |
client.Webhooks | Manage webhook endpoints, test, rotate secrets |
client.Topics | Manage subscription topics and preferences |
client.Properties | Manage contact property definitions and values |
Send Emails
emails.go
ctx := context.Background()
// Send with named sender
email, err := client.Emails.Send(ctx, &veilmail.SendEmailParams{
From: veilmail.FormatEmailAddress("Alice", "alice@yourdomain.com"),
To: []string{"bob@example.com"},
Subject: "Hello",
HTML: "<p>Hello Bob!</p>",
Tags: []string{"welcome"},
})
// Send with template
email, err = client.Emails.Send(ctx, &veilmail.SendEmailParams{
From: "hello@yourdomain.com",
To: []string{"user@example.com"},
TemplateID: "tmpl_xxx",
TemplateData: map[string]interface{}{
"name": "Alice",
},
})
// Batch send (up to 100)
result, err := client.Emails.SendBatch(ctx, []veilmail.SendEmailParams{
{From: "hello@yourdomain.com", To: []string{"user1@example.com"}, Subject: "Hi", HTML: "<p>Hi!</p>"},
{From: "hello@yourdomain.com", To: []string{"user2@example.com"}, Subject: "Hi", HTML: "<p>Hi!</p>"},
})
fmt.Printf("Sent: %d, Failed: %d\n", result.Successful, result.Failed)Subscriber Management
subscribers.go
// Get a subscribers service for an audience
subs := client.Audiences.Subscribers("audience_xxxxx")
// Add a subscriber
subscriber, err := subs.Add(ctx, &veilmail.AddSubscriberParams{
Email: "user@example.com",
FirstName: "Alice",
LastName: "Smith",
})
// List subscribers
result, err := subs.List(ctx, &veilmail.ListSubscribersParams{
ListParams: veilmail.ListParams{Limit: 50},
Status: veilmail.SubscriberStatusActive,
})
for _, sub := range result.Data {
fmt.Println(sub.Email)
}
// Bulk import
importResult, err := subs.Import(ctx, &veilmail.ImportSubscribersParams{
Subscribers: []veilmail.ImportSubscriberEntry{
{Email: "user1@example.com", FirstName: "Bob"},
{Email: "user2@example.com", FirstName: "Carol"},
},
})
fmt.Printf("Created: %d, Skipped: %d\n", importResult.Created, importResult.Skipped)
// Export as CSV
csv, err := subs.Export(ctx, nil)Error Handling
Use type-checking helper functions to handle specific error types:
errors.go
email, err := client.Emails.Send(ctx, params)
if err != nil {
if veilmail.IsAuthenticationError(err) {
log.Fatal("Invalid API key")
}
if veilmail.IsPiiDetectedError(err) {
var apiErr *veilmail.Error
if errors.As(err, &apiErr) {
log.Printf("PII detected: %v", apiErr.PiiTypes)
}
}
if veilmail.IsRateLimitError(err) {
var apiErr *veilmail.Error
if errors.As(err, &apiErr) {
log.Printf("Rate limited, retry after %d seconds", apiErr.RetryAfter)
}
}
if veilmail.IsValidationError(err) {
var apiErr *veilmail.Error
if errors.As(err, &apiErr) {
log.Printf("Validation: %s (details: %v)", apiErr.Message, apiErr.Details)
}
}
log.Fatal(err)
}Webhook Verification
The SDK provides a constant-time HMAC-SHA256 verification function:
webhook_handler.go
package main
import (
"encoding/json"
"io"
"log"
"net/http"
veilmail "github.com/Resonia-Health/veilmail-go"
)
const webhookSecret = "whsec_xxxxx"
func webhookHandler(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
signature := r.Header.Get("X-Signature-Hash")
if !veilmail.VerifySignature(body, signature, webhookSecret) {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}
var event map[string]interface{}
json.Unmarshal(body, &event)
switch event["type"] {
case "email.delivered":
log.Printf("Delivered: %v", event["data"])
case "email.bounced":
log.Printf("Bounced: %v", event["data"])
}
w.WriteHeader(http.StatusOK)
}
func main() {
http.HandleFunc("/webhooks/veilmail", webhookHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}Context Support
All API methods accept a context.Context as the first argument. Use this for request cancellation, timeouts, and passing request-scoped values. Pass context.Background() if you don't need context features.
Required Scopes
The Go SDK uses the same API scopes as the Authentication system. See the Node.js SDK documentation for the full scope reference.