Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.paubox.com/llms.txt

Use this file to discover all available pages before exploring further.

Creating a client

client, err := paubox.New(apiKey, username, ...opts)
paubox.New accepts optional functional options:
OptionDescription
paubox.WithBaseURL(url)Override the API base URL (useful for testing)
paubox.WithHTTPClient(c)Supply a custom *http.Client
paubox.WithTimeout(d)Set a per-request timeout (default: 30 s)
paubox.WithRetry(cfg)Configure retry behavior (see below)
paubox.WithUserAgent(s)Append a string to the User-Agent header

Retry configuration

By default the client retries GET requests up to 3 times on 429 and 5xx responses, with exponential backoff between 500 ms and 30 s.
client, err := paubox.New(apiKey, username,
    paubox.WithRetry(paubox.RetryConfig{
        MaxAttempts:        4,
        WaitMin:            200 * time.Millisecond,
        WaitMax:            5 * time.Second,
        RetryNonIdempotent: false, // set true to also retry POST/PATCH
    }),
)

Send a message

resp, err := client.SendMessage(ctx, &paubox.SendMessageRequest{
    Message:              msg,
    OverrideOpenTracking: true,  // optional
})

Message fields

paubox.Message{
    Recipients: []string{"alice@example.com"},          // required; To recipients
    CC:         []string{"manager@example.com"},         // optional
    BCC:        []string{"audit@example.com"},           // optional
    Headers: paubox.MessageHeaders{
        From:             "sender@yourdomain.com",       // required
        Subject:          "Your results are ready",      // required
        ReplyTo:          "support@yourdomain.com",      // optional
        ListUnsubscribe:  "<mailto:unsub@yourdomain.com>", // optional
    },
    Content: paubox.MessageContent{
        PlainText: paubox.Ptr("Plain text body."),       // at least one required
        HTML:      paubox.Ptr("<p>HTML body.</p>"),
    },
    Attachments: []paubox.Attachment{
        {
            FileName:    "report.pdf",
            ContentType: "application/pdf",
            Content:     base64EncodedBytes, // base64-encoded file content
        },
    },
    AllowNonTLS:             false, // default false — enforce TLS
    ForceSecureNotification: paubox.Ptr(true), // optional
}
The paubox.Ptr[T] helper creates a pointer to any value, which is required for optional fields typed as *T:
paubox.Ptr("some string")  // returns *string
paubox.Ptr(true)           // returns *bool

Response

type SendMessageResponse struct {
    SourceTrackingID string
    Data             SendMessageData
}

Send a batch

Send up to 50 messages in a single API call. Each message gets its own tracking ID.
resp, err := client.SendBatch(ctx, &paubox.SendBatchRequest{
    Messages: []paubox.Message{
        {
            Recipients: []string{"alice@example.com"},
            Headers:    paubox.MessageHeaders{From: "f@yourdomain.com", Subject: "Hi Alice"},
            Content:    paubox.MessageContent{PlainText: paubox.Ptr("Hello Alice")},
        },
        {
            Recipients: []string{"bob@example.com"},
            Headers:    paubox.MessageHeaders{From: "f@yourdomain.com", Subject: "Hi Bob"},
            Content:    paubox.MessageContent{PlainText: paubox.Ptr("Hello Bob")},
        },
    },
})
if err != nil {
    log.Fatal(err)
}

for i, msg := range resp.Messages {
    fmt.Printf("[%d] tracking ID: %s\n", i, msg.SourceTrackingID)
}

Check delivery status

disp, err := client.GetEmailDisposition(ctx, sourceTrackingID)
disp.Data.Message.MessageDeliveries is a slice of per-recipient records:
for _, d := range disp.Data.Message.MessageDeliveries {
    fmt.Printf("%s%s (opened: %d)\n",
        d.Recipient,
        d.Status.DeliveryStatus,
        d.OpenedCount,
    )
}
Common DeliveryStatus values: delivered, opened, failed, pending.

Dynamic templates

Templates use Handlebars syntax ({{variable_name}}).

Create a template

tmpl, err := client.CreateTemplate(ctx, &paubox.CreateTemplateRequest{
    Name: "appointment-confirmation",
    Body: []byte(`<p>Hello {{first_name}}, your appointment is on {{date}} at {{time}}.</p>`),
})

List templates

list, err := client.ListTemplates(ctx)
for _, t := range list.Templates {
    fmt.Println(t.ID, t.Name)
}

Get a template

tmpl, err := client.GetTemplate(ctx, "template-id")

Update a template

tmpl, err := client.UpdateTemplate(ctx, "template-id", &paubox.UpdateTemplateRequest{
    Name: "appointment-confirmation-v2",
    Body: []byte(`<p>Updated body for {{first_name}}.</p>`),
})

Delete a template

_, err = client.DeleteTemplate(ctx, "template-id")

Send a templated message

resp, err := client.SendTemplatedMessage(ctx, &paubox.SendTemplatedMessageRequest{
    TemplateName: "appointment-confirmation",
    TemplateValues: map[string]any{
        "first_name": "Jane",
        "date":       "2024-03-15",
        "time":       "2:00 PM",
    },
    Message: paubox.TemplatedMessage{
        Recipients: []string{"jane@example.com"},
        Headers: paubox.MessageHeaders{
            From:    "appointments@yourclinic.com",
            Subject: "Your appointment is confirmed",
        },
    },
})

Error handling

All methods return a typed error. Use errors.As to inspect details and errors.Is to match sentinels:
resp, err := client.SendMessage(ctx, req)
if err != nil {
    // Match a specific condition
    if errors.Is(err, paubox.ErrUnauthorized) {
        log.Fatal("invalid API key or username")
    }
    if errors.Is(err, paubox.ErrRateLimit) {
        log.Fatal("rate limited — back off and retry")
    }

    // Inspect the full error
    var apiErr *paubox.PauboxError
    if errors.As(err, &apiErr) {
        fmt.Printf("HTTP %d: %s%s (request ID: %s)\n",
            apiErr.StatusCode, apiErr.Title, apiErr.Details, apiErr.RequestID)
    }

    log.Fatal(err)
}

Sentinel errors

SentinelHTTP status
paubox.ErrBadRequest400
paubox.ErrUnauthorized401
paubox.ErrForbidden403
paubox.ErrNotFound404
paubox.ErrRateLimit429
paubox.ErrServerError5xx