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-go

Quick 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:

ResourceDescription
client.EmailsSend, batch send, list, get, cancel, update emails
client.DomainsCreate, verify, update, list, delete domains
client.TemplatesCreate, update, preview, list, delete templates
client.AudiencesManage audiences and subscribers
client.CampaignsCreate, schedule, send, pause, resume, cancel campaigns
client.WebhooksManage webhook endpoints, test, rotate secrets
client.TopicsManage subscription topics and preferences
client.PropertiesManage 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.