lettermint

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 23, 2025 License: MIT Imports: 15 Imported by: 0

README

Lettermint Go SDK

Build Release Go Reference

The official Go SDK for Lettermint.

Requirements

  • Go 1.21 or higher

Installation

go get github.com/lettermint/lettermint-go

Usage

Initialize the SDK
import lettermint "github.com/lettermint/lettermint-go"

client, err := lettermint.New("your-api-token")
if err != nil {
    log.Fatal(err)
}
Sending Emails

The SDK provides a fluent interface for sending emails:

ctx := context.Background()
resp, err := client.Email(ctx).
    From("sender@example.com").
    To("recipient@example.com").
    Subject("Hello from Lettermint").
    Text("This is a test email sent using the Lettermint Go SDK.").
    Send()

if err != nil {
    log.Fatal(err)
}

fmt.Printf("Email sent with ID: %s\n", resp.MessageID)
fmt.Printf("Status: %s\n", resp.Status)
Advanced Email Options
resp, err := client.Email(ctx).
    From("John Doe <sender@example.com>").
    To("recipient1@example.com", "recipient2@example.com").
    CC("cc@example.com").
    BCC("bcc@example.com").
    ReplyTo("reply@example.com").
    Subject("Hello from Lettermint").
    HTML("<h1>Hello</h1><p>This is an HTML email.</p>").
    Text("This is a plain text version of the email.").
    Headers(map[string]string{
        "X-Custom-Header": "Custom Value",
    }).
    Attach("attachment.txt", base64EncodedContent).
    AttachWithContentID("logo.png", base64EncodedLogo, "logo"). // Inline attachment
    IdempotencyKey("unique-id-123").
    Metadata(map[string]string{
        "user_id": "12345",
    }).
    Tag("campaign-123").
    Send()
Inline Attachments

You can embed images and other content in your HTML emails using Content-IDs:

resp, err := client.Email(ctx).
    From("sender@example.com").
    To("recipient@example.com").
    Subject("Email with inline image").
    HTML(`<p>Here is an image: <img src="cid:logo"></p>`).
    AttachWithContentID("logo.png", base64EncodedImage, "logo").
    Send()
Idempotency

To ensure that duplicate requests are not processed, you can use an idempotency key:

resp, err := client.Email(ctx).
    From("sender@example.com").
    To("recipient@example.com").
    Subject("Hello from Lettermint").
    Text("Hello! This is a test email.").
    IdempotencyKey("unique-request-id-123").
    Send()

The idempotency key should be a unique string that you generate for each unique email you want to send. If you make the same request with the same idempotency key, the API will return the same response without sending a duplicate email.

For more information, refer to the documentation.

Webhook Verification

Verify webhook signatures to ensure the authenticity of webhook requests:

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    event, err := lettermint.VerifyWebhookFromRequest(
        r,
        "your-webhook-secret",
        lettermint.DefaultWebhookTolerance,
    )
    if err != nil {
        http.Error(w, "Invalid signature", http.StatusUnauthorized)
        return
    }

    // Process the event
    switch event.Event {
    case "message.delivered":
        log.Printf("Email delivered to %s", event.Data.Recipient)
    case "message.hard_bounced":
        log.Printf("Hard bounce for %s", event.Data.Recipient)
    case "message.soft_bounced":
        log.Printf("Soft bounce for %s", event.Data.Recipient)
    }

    w.WriteHeader(http.StatusOK)
}

API Reference

Client Configuration
client, err := lettermint.New("your-api-token",
    lettermint.WithBaseURL("https://api.lettermint.co/v1"), // Optional
    lettermint.WithTimeout(30*time.Second),                  // Optional
    lettermint.WithHTTPClient(customHTTPClient),             // Optional
)
Email Builder Methods
  • From(email string): Set the sender email address
  • To(emails ...string): Set one or more recipient email addresses
  • Subject(subject string): Set the email subject
  • HTML(html string): Set the HTML body of the email
  • Text(text string): Set the plain text body of the email
  • CC(emails ...string): Set one or more CC email addresses
  • BCC(emails ...string): Set one or more BCC email addresses
  • ReplyTo(emails ...string): Set one or more Reply-To email addresses
  • Header(key, value string): Set a custom header
  • Headers(headers map[string]string): Set multiple custom headers
  • Attach(filename, base64Content string): Attach a file
  • AttachWithContentID(filename, content, contentID string): Attach an inline file
  • Route(route string): Set the routing key
  • IdempotencyKey(key string): Set an idempotency key
  • Metadata(metadata map[string]string): Set metadata
  • MetadataValue(key, value string): Set a single metadata value
  • Tag(tag string): Set a tag
  • Send() (*SendResponse, error): Send the email
Error Handling

The SDK provides structured error types:

resp, err := client.Email(ctx).From("...").To("...").Subject("...").HTML("...").Send()
if err != nil {
    // Check for specific error types
    var apiErr *lettermint.APIError
    if errors.As(err, &apiErr) {
        fmt.Printf("API Error (%d): %s\n", apiErr.StatusCode, apiErr.Message)
        fmt.Printf("Validation errors: %v\n", apiErr.Errors)
    }

    // Check for error categories using errors.Is()
    if errors.Is(err, lettermint.ErrValidation) {
        // Handle validation errors (422)
    } else if errors.Is(err, lettermint.ErrUnauthorized) {
        // Handle authentication errors (401)
    } else if errors.Is(err, lettermint.ErrTimeout) {
        // Handle timeout errors
    } else if errors.Is(err, lettermint.ErrRateLimited) {
        // Handle rate limit errors (429)
    }
}

Testing

go test ./...

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.

Documentation

Overview

Package lettermint provides the official Go SDK for the Lettermint email API.

Getting Started

Create a client with your API token:

client, err := lettermint.New("your-api-token")
if err != nil {
    log.Fatal(err)
}

Sending Emails

Use the fluent builder interface to compose and send emails:

ctx := context.Background()
resp, err := client.Email(ctx).
    From("sender@example.com").
    To("recipient@example.com").
    Subject("Hello from Lettermint").
    HTML("<p>Hello World</p>").
    Send()

The builder supports all email features including CC, BCC, attachments, metadata, tags, and idempotency keys:

resp, err := client.Email(ctx).
    From("John Doe <john@example.com>").
    To("user@example.com").
    CC("manager@example.com").
    Subject("Monthly Report").
    HTML("<h1>Report</h1>").
    Text("Report (plain text)").
    Attach("report.pdf", base64Content).
    MetadataValue("user_id", "12345").
    Tag("monthly-report").
    IdempotencyKey("report-dec-2024").
    Send()

Client Configuration

Use functional options to customize the client:

client, err := lettermint.New("your-api-token",
    lettermint.WithTimeout(60*time.Second),
    lettermint.WithBaseURL("https://custom-api.example.com"),
)

Error Handling

The SDK provides structured errors for easy handling:

resp, err := client.Email(ctx).From("...").To("...").Subject("...").HTML("...").Send()
if err != nil {
    // Check for specific error types
    var apiErr *lettermint.APIError
    if errors.As(err, &apiErr) {
        fmt.Printf("API error %d: %s\n", apiErr.StatusCode, apiErr.Message)
    }

    // Check for error categories
    if errors.Is(err, lettermint.ErrValidation) {
        // Handle validation errors
    }
}

Webhook Verification

Verify webhook signatures to ensure authenticity:

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    event, err := lettermint.VerifyWebhookFromRequest(
        r,
        "your-webhook-secret",
        lettermint.DefaultWebhookTolerance,
    )
    if err != nil {
        http.Error(w, "Invalid signature", http.StatusUnauthorized)
        return
    }

    // Process event
    switch event.Event {
    case "message.delivered":
        // Handle delivery
    case "message.bounced":
        // Handle bounce
    }
}

For more information, visit https://docs.lettermint.co

Index

Examples

Constants

View Source
const (
	// DefaultBaseURL is the default Lettermint API base URL.
	DefaultBaseURL = "https://api.lettermint.co/v1"

	// DefaultTimeout is the default HTTP client timeout.
	DefaultTimeout = 30 * time.Second

	// Version is the SDK version.
	Version = "1.0.0"
)
View Source
const (
	// DefaultWebhookTolerance is the default timestamp tolerance for webhook verification.
	// Webhooks with timestamps older than this will be rejected.
	DefaultWebhookTolerance = 5 * time.Minute

	// HeaderSignature is the webhook signature header name.
	HeaderSignature = "X-Lettermint-Signature"

	// HeaderDelivery is the webhook delivery timestamp header name.
	HeaderDelivery = "X-Lettermint-Delivery"
)

Variables

View Source
var (
	// ErrInvalidAPIToken indicates the API token is missing or invalid.
	ErrInvalidAPIToken = errors.New("lettermint: invalid or missing API token")

	// ErrInvalidRequest indicates request validation failed before sending.
	ErrInvalidRequest = errors.New("lettermint: invalid request")

	// ErrUnauthorized indicates authentication failed (HTTP 401).
	ErrUnauthorized = errors.New("lettermint: unauthorized")

	// ErrValidation indicates validation error from API (HTTP 422).
	ErrValidation = errors.New("lettermint: validation error")

	// ErrRateLimited indicates rate limit exceeded (HTTP 429).
	ErrRateLimited = errors.New("lettermint: rate limit exceeded")

	// ErrServerError indicates server error (HTTP 5xx).
	ErrServerError = errors.New("lettermint: server error")

	// ErrTimeout indicates request timeout.
	ErrTimeout = errors.New("lettermint: request timeout")

	// ErrInvalidWebhookSignature indicates webhook signature verification failed.
	ErrInvalidWebhookSignature = errors.New("lettermint: invalid webhook signature")

	// ErrWebhookTimestampExpired indicates webhook timestamp is outside tolerance window.
	ErrWebhookTimestampExpired = errors.New("lettermint: webhook timestamp outside tolerance window")
)

Sentinel errors for type checking with errors.Is()

Functions

This section is empty.

Types

type APIError

type APIError struct {
	// StatusCode is the HTTP status code returned by the API.
	StatusCode int

	// Message is the error message from the API.
	Message string

	// ErrorType is the specific error type (e.g., "validation_error").
	ErrorType string

	// Errors contains field-specific validation errors.
	Errors map[string][]string

	// ResponseBody is the raw response body for debugging.
	ResponseBody string
}

APIError represents an error response from the Lettermint API.

func (*APIError) Error

func (e *APIError) Error() string

Error implements the error interface.

func (*APIError) Unwrap

func (e *APIError) Unwrap() error

Unwrap returns the underlying sentinel error for use with errors.Is().

type Attachment

type Attachment struct {
	// Filename is the name of the attachment file.
	Filename string `json:"filename"`

	// Content is the base64-encoded content of the attachment.
	Content string `json:"content"`

	// ContentID is the Content-ID for inline attachments (optional).
	// Used for embedding images in HTML via cid: references.
	ContentID string `json:"content_id,omitempty"`
}

Attachment represents an email attachment.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the main Lettermint SDK client.

The client is safe for concurrent use by multiple goroutines. Create a new client using the New function.

func New

func New(apiToken string, opts ...Option) (*Client, error)

New creates a new Lettermint client with the given API token and options.

The API token is required and can be obtained from the Lettermint dashboard. Returns an error if the API token is empty.

Example:

client, err := lettermint.New("your-api-token")
if err != nil {
    log.Fatal(err)
}

With options:

client, err := lettermint.New("your-api-token",
    lettermint.WithBaseURL("https://custom-api.example.com"),
    lettermint.WithTimeout(60*time.Second),
)
Example
package main

import (
	"log"

	lettermint "github.com/lettermint/lettermint-go"
)

func main() {
	// Create a new Lettermint client
	client, err := lettermint.New("your-api-token")
	if err != nil {
		log.Fatal(err)
	}
	_ = client // Use client...
}
Example (WithOptions)
package main

import (
	"log"

	lettermint "github.com/lettermint/lettermint-go"
)

func main() {
	// Create a client with custom options
	client, err := lettermint.New("your-api-token",
		lettermint.WithBaseURL("https://api.lettermint.co/v1"),
		lettermint.WithTimeout(60_000_000_000), // 60 seconds in nanoseconds
	)
	if err != nil {
		log.Fatal(err)
	}
	_ = client // Use client...
}

func (*Client) Email

func (c *Client) Email(ctx context.Context) *EmailBuilder

Email creates a new email builder for composing and sending emails.

The builder uses a fluent interface for constructing emails. Call Send() on the builder to send the email.

Example:

resp, err := client.Email(ctx).
    From("sender@example.com").
    To("recipient@example.com").
    Subject("Hello").
    HTML("<p>World</p>").
    Send()
Example
package main

import (
	"context"
	"fmt"
	"log"

	lettermint "github.com/lettermint/lettermint-go"
)

func main() {
	client, err := lettermint.New("your-api-token")
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()
	resp, err := client.Email(ctx).
		From("sender@example.com").
		To("recipient@example.com").
		Subject("Hello from Lettermint").
		Text("This is a test email.").
		Send()

	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Email sent with ID: %s\n", resp.MessageID)
}
Example (Advanced)
package main

import (
	"context"
	"fmt"
	"log"

	lettermint "github.com/lettermint/lettermint-go"
)

func main() {
	client, err := lettermint.New("your-api-token")
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()
	resp, err := client.Email(ctx).
		From("John Doe <john@example.com>").
		To("recipient@example.com").
		CC("manager@example.com").
		BCC("archive@example.com").
		ReplyTo("support@example.com").
		Subject("Monthly Report").
		HTML("<h1>Report</h1><p>Content here</p>").
		Text("Report\n\nContent here").
		Header("X-Campaign-ID", "dec-2024").
		Metadata(map[string]string{
			"user_id":     "12345",
			"campaign_id": "dec-2024",
		}).
		Tag("monthly-report").
		IdempotencyKey("report-dec-2024").
		Send()

	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Email sent with ID: %s, Status: %s\n", resp.MessageID, resp.Status)
}

type EmailBuilder

type EmailBuilder struct {
	// contains filtered or unexported fields
}

EmailBuilder provides a fluent interface for composing and sending emails.

Create a new EmailBuilder using Client.Email(ctx). The builder is NOT safe for concurrent use; create a new builder for each email.

func (*EmailBuilder) Attach

func (b *EmailBuilder) Attach(filename, content string) *EmailBuilder

Attach adds a file attachment to the email.

The content must be base64-encoded.

func (*EmailBuilder) AttachWithContentID

func (b *EmailBuilder) AttachWithContentID(filename, content, contentID string) *EmailBuilder

AttachWithContentID adds a file attachment with a Content-ID for inline embedding.

Use this for embedding images in HTML emails via cid: references. Example: <img src="cid:logo"> with contentID "logo".

func (*EmailBuilder) BCC

func (b *EmailBuilder) BCC(emails ...string) *EmailBuilder

BCC adds one or more BCC recipient email addresses.

Can be called multiple times to add more BCC recipients.

func (*EmailBuilder) CC

func (b *EmailBuilder) CC(emails ...string) *EmailBuilder

CC adds one or more CC recipient email addresses.

Can be called multiple times to add more CC recipients.

func (*EmailBuilder) From

func (b *EmailBuilder) From(email string) *EmailBuilder

From sets the sender email address.

Supports RFC 5322 format: "John Doe <john@example.com>" or plain "john@example.com".

func (*EmailBuilder) HTML

func (b *EmailBuilder) HTML(html string) *EmailBuilder

HTML sets the HTML body content.

At least one of HTML or Text must be set before sending.

func (*EmailBuilder) Header

func (b *EmailBuilder) Header(key, value string) *EmailBuilder

Header adds a single custom email header.

Can be called multiple times to add more headers.

func (*EmailBuilder) Headers

func (b *EmailBuilder) Headers(headers map[string]string) *EmailBuilder

Headers sets multiple custom email headers at once.

Merges with any headers already set via Header().

func (*EmailBuilder) IdempotencyKey

func (b *EmailBuilder) IdempotencyKey(key string) *EmailBuilder

IdempotencyKey sets an idempotency key to prevent duplicate sends.

If you provide the same idempotency key for multiple requests, only the first one will be processed. Use this when retrying failed requests.

func (*EmailBuilder) Metadata

func (b *EmailBuilder) Metadata(metadata map[string]string) *EmailBuilder

Metadata sets custom metadata key-value pairs.

Metadata is included in webhook payloads but not in email headers. Merges with any metadata already set.

func (*EmailBuilder) MetadataValue

func (b *EmailBuilder) MetadataValue(key, value string) *EmailBuilder

MetadataValue sets a single metadata key-value pair.

Can be called multiple times to add more metadata.

func (*EmailBuilder) ReplyTo

func (b *EmailBuilder) ReplyTo(emails ...string) *EmailBuilder

ReplyTo sets one or more Reply-To email addresses.

Can be called multiple times to add more Reply-To addresses.

func (*EmailBuilder) Route

func (b *EmailBuilder) Route(route string) *EmailBuilder

Route sets the routing key for the email.

Routes determine which sending configuration to use.

func (*EmailBuilder) Send

func (b *EmailBuilder) Send() (*SendResponse, error)

Send sends the composed email via the Lettermint API.

Returns the send response containing the message ID and status, or an error if the request fails.

The context passed to Email() controls the request lifecycle. Use context.WithTimeout() or context.WithDeadline() for custom timeouts.

func (*EmailBuilder) Subject

func (b *EmailBuilder) Subject(subject string) *EmailBuilder

Subject sets the email subject line.

func (*EmailBuilder) Tag

func (b *EmailBuilder) Tag(tag string) *EmailBuilder

Tag sets an email tag for categorization.

Tags can be used to filter and group emails in the Lettermint dashboard.

func (*EmailBuilder) Text

func (b *EmailBuilder) Text(text string) *EmailBuilder

Text sets the plain text body content.

At least one of HTML or Text must be set before sending.

func (*EmailBuilder) To

func (b *EmailBuilder) To(emails ...string) *EmailBuilder

To adds one or more recipient email addresses.

Can be called multiple times to add more recipients.

type Option

type Option func(*Client)

Option is a functional option for configuring the Client.

func WithBaseURL

func WithBaseURL(baseURL string) Option

WithBaseURL sets a custom base URL for the Lettermint API.

By default, the client uses https://api.lettermint.co/v1. Use this option for testing or if you have a custom API endpoint.

func WithHTTPClient

func WithHTTPClient(client *http.Client) Option

WithHTTPClient sets a custom HTTP client.

Use this option to configure custom transport settings, proxies, or to inject a mock client for testing.

Note: If you set a custom HTTP client, the WithTimeout option will modify the timeout of the provided client.

func WithTimeout

func WithTimeout(timeout time.Duration) Option

WithTimeout sets the HTTP client timeout for all requests.

By default, the timeout is 30 seconds.

type SendResponse

type SendResponse struct {
	// MessageID is the unique identifier for the sent message.
	MessageID string `json:"message_id"`

	// Status is the current status of the message.
	// Possible values: pending, queued, processed, delivered, soft_bounced, hard_bounced, failed
	Status string `json:"status"`
}

SendResponse represents the response from the send email API.

type WebhookEvent

type WebhookEvent struct {
	// ID is the unique webhook delivery ID.
	ID string `json:"id"`

	// Event is the event type (e.g., "message.delivered", "message.bounced").
	Event string `json:"event"`

	// Timestamp is the Unix timestamp when the event occurred.
	Timestamp int64 `json:"timestamp"`

	// Data contains the event-specific data.
	Data WebhookEventData `json:"data"`

	// RawPayload contains the original JSON payload for custom parsing.
	RawPayload []byte `json:"-"`
}

WebhookEvent represents a parsed webhook payload from Lettermint.

func VerifyWebhook

func VerifyWebhook(signature string, payload []byte, deliveryTimestamp int64, signingSecret string, tolerance time.Duration) (*WebhookEvent, error)

VerifyWebhook verifies a webhook signature and returns the parsed event.

The signature format is: t={timestamp},v1={hmac_sha256_hex} The HMAC is computed over: {timestamp}.{payload}

Parameters:

  • signature: The X-Lettermint-Signature header value
  • payload: The raw request body
  • deliveryTimestamp: The X-Lettermint-Delivery header value (Unix timestamp), or 0 to skip cross-validation
  • signingSecret: Your webhook signing secret from the Lettermint dashboard
  • tolerance: Maximum age of the webhook timestamp (use DefaultWebhookTolerance)

Returns the parsed webhook event or an error if verification fails.

func VerifyWebhookFromRequest

func VerifyWebhookFromRequest(r *http.Request, signingSecret string, tolerance time.Duration) (*WebhookEvent, error)

VerifyWebhookFromRequest verifies a webhook from an HTTP request.

This is a convenience function that extracts the signature and payload from the request and calls VerifyWebhook.

Note: This function reads and closes the request body.

Example:

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    event, err := lettermint.VerifyWebhookFromRequest(r, "your-signing-secret", lettermint.DefaultWebhookTolerance)
    if err != nil {
        http.Error(w, "Invalid signature", http.StatusUnauthorized)
        return
    }
    // Process event...
}
Example
package main

import (
	"fmt"
	"net/http"

	lettermint "github.com/lettermint/lettermint-go"
)

func main() {
	http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
		event, err := lettermint.VerifyWebhookFromRequest(
			r,
			"your-webhook-secret",
			lettermint.DefaultWebhookTolerance,
		)
		if err != nil {
			http.Error(w, "Invalid signature", http.StatusUnauthorized)
			return
		}

		// Process the event
		switch event.Event {
		case "message.delivered":
			fmt.Printf("Email delivered to %s\n", event.Data.Recipient)
		case "message.hard_bounced":
			fmt.Printf("Hard bounce for %s\n", event.Data.Recipient)
		}

		w.WriteHeader(http.StatusOK)
	})
}

type WebhookEventData

type WebhookEventData struct {
	// MessageID is the unique identifier of the related message.
	MessageID string `json:"message_id"`

	// Recipient is the email address of the recipient.
	Recipient string `json:"recipient"`

	// Tag is the tag associated with the message (if set).
	Tag string `json:"tag,omitempty"`

	// Metadata contains the custom metadata associated with the message.
	Metadata map[string]string `json:"metadata,omitempty"`

	// Response contains delivery response details (for delivered/bounced events).
	Response *WebhookResponse `json:"response,omitempty"`
}

WebhookEventData contains the event-specific data in a webhook payload.

type WebhookResponse

type WebhookResponse struct {
	// StatusCode is the SMTP response status code.
	StatusCode int `json:"status_code,omitempty"`

	// Message is the SMTP response message.
	Message string `json:"message,omitempty"`
}

WebhookResponse contains delivery response details.

Directories

Path Synopsis
examples
advanced command
Example: Advanced email sending with Lettermint
Example: Advanced email sending with Lettermint
basic command
Example: Basic email sending with Lettermint
Example: Basic email sending with Lettermint
webhook command
Example: Webhook verification with Lettermint
Example: Webhook verification with Lettermint

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL