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:
| Option | Description |
|---|
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
| Sentinel | HTTP status |
|---|
paubox.ErrBadRequest | 400 |
paubox.ErrUnauthorized | 401 |
paubox.ErrForbidden | 403 |
paubox.ErrNotFound | 404 |
paubox.ErrRateLimit | 429 |
paubox.ErrServerError | 5xx |