> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lettr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Advanced

> Advanced Lettr Go SDK usage including CC and BCC, reply-to, attachments, templates, batch sending, and error handling.

This guide covers advanced features of the Lettr Go SDK including attachments, templates, batch sending, error handling, and best practices for production applications.

<Note>
  New to the Go SDK? Start with the [Go Quickstart](/quickstart/go/quickstart) to learn the basics first.
</Note>

## Advanced Features

### CC and BCC Recipients

Add CC and BCC recipients:

```go theme={null}
resp, err := client.Emails.Send(ctx, &lettr.SendEmailRequest{
    From:    "notifications@yourdomain.com",
    To:      []string{"primary@example.com"},
    Cc:      []string{"cc@example.com", "cc2@example.com"},
    Bcc:     []string{"bcc@example.com"},
    Subject: "Team notification",
    Html:    "<p>This email has CC and BCC recipients.</p>",
})
```

<Note>
  BCC recipients are hidden from all other recipients. They receive the email but their addresses are not visible in the headers.
</Note>

### Reply-To Address

Specify a different reply-to address:

```go theme={null}
resp, err := client.Emails.Send(ctx, &lettr.SendEmailRequest{
    From:    "notifications@yourdomain.com",
    ReplyTo: "support@yourdomain.com",
    To:      []string{"user@example.com"},
    Subject: "Support notification",
    Html:    "<p>Click reply to contact our support team.</p>",
})
```

### Attachments

Add file attachments to your emails:

```go theme={null}
import (
    "encoding/base64"
    "os"
)

// Read file and encode to base64
fileData, err := os.ReadFile("invoice.pdf")
if err != nil {
    log.Fatal(err)
}
encodedContent := base64.StdEncoding.EncodeToString(fileData)

resp, err := client.Emails.Send(ctx, &lettr.SendEmailRequest{
    From:    "billing@yourdomain.com",
    To:      []string{"customer@example.com"},
    Subject: "Your invoice",
    Html:    "<p>Please find your invoice attached.</p>",
    Attachments: []lettr.Attachment{
        {
            Name: "invoice.pdf",
            Data: encodedContent,
            Type: "application/pdf",
        },
    },
})
```

Attach multiple files:

```go theme={null}
attachments := []lettr.Attachment{}

// Attach PDF
pdfData, _ := os.ReadFile("document.pdf")
attachments = append(attachments, lettr.Attachment{
    Name: "document.pdf",
    Data: base64.StdEncoding.EncodeToString(pdfData),
    Type: "application/pdf",
})

// Attach image
imgData, _ := os.ReadFile("logo.png")
attachments = append(attachments, lettr.Attachment{
    Name: "logo.png",
    Data: base64.StdEncoding.EncodeToString(imgData),
    Type: "image/png",
})

resp, err := client.Emails.Send(ctx, &lettr.SendEmailRequest{
    From:        "sender@yourdomain.com",
    To:          []string{"recipient@example.com"},
    Subject:     "Files attached",
    Html:        "<p>See attached files.</p>",
    Attachments: attachments,
})
```

<Warning>
  Attachments must be base64-encoded. The total size of all attachments should not exceed 10MB. Larger files should be hosted and linked instead.
</Warning>

### Templates

Send emails using Lettr-managed templates:

```go theme={null}
resp, err := client.Emails.Send(ctx, &lettr.SendEmailRequest{
    From:         "notifications@yourdomain.com",
    To:           []string{"user@example.com"},
    TemplateSlug: "welcome-email",
    SubstitutionData: map[string]string{
        "name":          "John Doe",
        "company":       "Acme Corp",
        "verify_url":    "https://example.com/verify/abc123",
        "support_email": "support@yourdomain.com",
    },
})
```

<Tip>
  Templates are managed in the [Lettr dashboard](https://app.lettr.com/templates). Use merge tags to personalize content without rebuilding HTML in your code.
</Tip>

### Custom Headers

Add custom email headers:

```go theme={null}
resp, err := client.Emails.Send(ctx, &lettr.SendEmailRequest{
    From:    "notifications@yourdomain.com",
    To:      []string{"user@example.com"},
    Subject: "Custom headers example",
    Html:    "<p>This email has custom headers.</p>",
    Headers: map[string]string{
        "X-Campaign-ID":   "summer-2024",
        "X-Priority":      "high",
        "X-Mailer":        "Acme Mailer v1.0",
    },
})
```

### Tracking

Enable open and click tracking:

```go theme={null}
resp, err := client.Emails.Send(ctx, &lettr.SendEmailRequest{
    From:    "marketing@yourdomain.com",
    To:      []string{"user@example.com"},
    Subject: "Newsletter",
    Html:    "<p>Check out <a href='https://example.com'>our website</a>!</p>",
    Options: &lettr.EmailOptions{
        OpenTracking:  true,
        ClickTracking: true,
    },
})
```

<Info>
  Open tracking works by embedding a transparent pixel image. Click tracking rewrites links to go through Lettr's tracking domain. Both features respect user privacy and comply with email regulations.
</Info>

### Metadata

Attach custom metadata for tracking and filtering:

```go theme={null}
resp, err := client.Emails.Send(ctx, &lettr.SendEmailRequest{
    From:    "notifications@yourdomain.com",
    To:      []string{"user@example.com"},
    Subject: "Order confirmation",
    Html:    "<p>Your order has been confirmed.</p>",
    Metadata: map[string]string{
        "user_id":    "12345",
        "order_id":   "ORD-98765",
        "campaign":   "abandoned-cart",
        "environment": "production",
    },
})
```

Metadata is returned in webhook events and can be used to correlate emails with your application data.

## Error Handling

The SDK provides structured error responses for different error scenarios:

```go theme={null}
resp, err := client.Emails.Send(ctx, &lettr.SendEmailRequest{
    From:    "invalid@unverified-domain.com",
    To:      []string{"user@example.com"},
    Subject: "Test",
    Html:    "<p>Test</p>",
})

if err != nil {
    switch e := err.(type) {
    case *lettr.ValidationError:
        // 422 validation errors
        log.Printf("Validation failed: %v", e.Message)
        for field, messages := range e.Errors {
            log.Printf("  %s: %v", field, messages)
        }
    case *lettr.AuthError:
        // 401 authentication errors
        log.Printf("Authentication failed: %v", e.Message)
    case *lettr.RateLimitError:
        // 429 rate limit errors
        log.Printf("Rate limit exceeded: %v", e.Message)
    case *lettr.APIError:
        // Other API errors (500, 503, etc.)
        log.Printf("API error: %v", e.Message)
    default:
        // Network or other errors
        log.Printf("Request failed: %v", err)
    }
    return
}
```

### Common Error Scenarios

**Unverified domain:**

* Error: 422 Validation Error
* Message: "The from address domain is not verified"
* Solution: Verify your domain in the dashboard

**Invalid API key:**

* Error: 401 Authentication Error
* Message: "Invalid API key"
* Solution: Check your API key is correct and active

**Rate limit exceeded:**

* Error: 429 Rate Limit Error
* Message: "Too many requests"
* Solution: Implement exponential backoff retry logic

## Batch Sending

Send multiple emails efficiently using goroutines:

```go theme={null}
package main

import (
    "context"
    "log"
    "sync"

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

func main() {
    client := lettr.NewClient(os.Getenv("LETTR_API_KEY"))
    ctx := context.Background()

    recipients := []string{
        "user1@example.com",
        "user2@example.com",
        "user3@example.com",
    }

    var wg sync.WaitGroup

    for _, recipient := range recipients {
        wg.Add(1)

        go func(to string) {
            defer wg.Done()

            resp, err := client.Emails.Send(ctx, &lettr.SendEmailRequest{
                From:    "notifications@yourdomain.com",
                To:      []string{to},
                Subject: "Batch notification",
                Html:    "<p>This is a batch email.</p>",
            })

            if err != nil {
                log.Printf("Failed to send to %s: %v", to, err)
                return
            }

            log.Printf("Sent to %s (Request ID: %s)", to, resp.Data.RequestID)
        }(recipient)
    }

    wg.Wait()
    log.Println("All emails sent")
}
```

### With Rate Limiting

Use a semaphore to limit concurrent requests:

```go theme={null}
func sendBatch(client *lettr.Client, recipients []string, maxConcurrent int) {
    ctx := context.Background()
    sem := make(chan struct{}, maxConcurrent)
    var wg sync.WaitGroup

    for _, recipient := range recipients {
        wg.Add(1)

        go func(to string) {
            defer wg.Done()

            // Acquire semaphore
            sem <- struct{}{}
            defer func() { <-sem }()

            resp, err := client.Emails.Send(ctx, &lettr.SendEmailRequest{
                From:    "notifications@yourdomain.com",
                To:      []string{to},
                Subject: "Batch email",
                Html:    "<p>Hello!</p>",
            })

            if err != nil {
                log.Printf("Failed: %v", err)
                return
            }

            log.Printf("Sent to %s", to)
        }(recipient)
    }

    wg.Wait()
}

// Usage
sendBatch(client, recipients, 10) // Max 10 concurrent requests
```

<Tip>
  For large batches, consider using a worker pool pattern or a job queue to manage concurrent requests and handle failures gracefully.
</Tip>

## Context and Timeouts

All SDK methods accept `context.Context` for cancellation and timeouts:

```go theme={null}
import (
    "context"
    "time"
)

// Set a 10-second timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

resp, err := client.Emails.Send(ctx, &lettr.SendEmailRequest{
    From:    "sender@yourdomain.com",
    To:      []string{"user@example.com"},
    Subject: "Time-sensitive email",
    Html:    "<p>This request will timeout after 10 seconds.</p>",
})

if err != nil {
    if ctx.Err() == context.DeadlineExceeded {
        log.Println("Request timed out")
    } else {
        log.Printf("Request failed: %v", err)
    }
    return
}
```

### Cancellation

Cancel requests when they're no longer needed:

```go theme={null}
ctx, cancel := context.WithCancel(context.Background())

// Start request in goroutine
go func() {
    resp, err := client.Emails.Send(ctx, &lettr.SendEmailRequest{
        From:    "sender@yourdomain.com",
        To:      []string{"user@example.com"},
        Subject: "Cancellable email",
        Html:    "<p>This can be cancelled.</p>",
    })

    if err != nil {
        if ctx.Err() == context.Canceled {
            log.Println("Request was cancelled")
        }
    }
}()

// Cancel after 1 second
time.Sleep(1 * time.Second)
cancel()
```

## Best Practices

### Use Environment Variables

Never hardcode API keys. Use environment variables or a secrets manager:

```go theme={null}
apiKey := os.Getenv("LETTR_API_KEY")
if apiKey == "" {
    log.Fatal("LETTR_API_KEY is required")
}

client := lettr.NewClient(apiKey)
```

### Validate Before Sending

Validate email addresses before making API calls to avoid unnecessary errors:

```go theme={null}
import "net/mail"

func isValidEmail(email string) bool {
    _, err := mail.ParseAddress(email)
    return err == nil
}

if !isValidEmail(recipient) {
    log.Printf("Invalid email address: %s", recipient)
    return
}
```

### Log Request IDs

Always log the `RequestID` from successful sends for tracking and debugging:

```go theme={null}
resp, err := client.Emails.Send(ctx, req)
if err != nil {
    log.Printf("Send failed: %v", err)
    return
}

log.Printf("Email sent successfully - Request ID: %s, Accepted: %d",
    resp.Data.RequestID, resp.Data.Accepted)
```

### Handle Errors Gracefully

Implement retry logic for transient errors:

```go theme={null}
import "time"

func sendWithRetry(client *lettr.Client, req *lettr.SendEmailRequest, maxRetries int) error {
    ctx := context.Background()

    for attempt := 0; attempt <= maxRetries; attempt++ {
        resp, err := client.Emails.Send(ctx, req)
        if err == nil {
            log.Printf("Email sent: %s", resp.Data.RequestID)
            return nil
        }

        // Don't retry validation errors
        if _, ok := err.(*lettr.ValidationError); ok {
            return err
        }

        // Retry on rate limit or server errors
        if attempt < maxRetries {
            backoff := time.Duration(attempt+1) * time.Second
            log.Printf("Attempt %d failed, retrying in %v: %v", attempt+1, backoff, err)
            time.Sleep(backoff)
            continue
        }

        return err
    }

    return nil
}
```

### Use Structured Logging

Use structured logging for better observability:

```go theme={null}
import "log/slog"

logger := slog.Default()

resp, err := client.Emails.Send(ctx, req)
if err != nil {
    logger.Error("Failed to send email",
        "error", err,
        "to", req.To,
        "subject", req.Subject,
    )
    return
}

logger.Info("Email sent successfully",
    "request_id", resp.Data.RequestID,
    "accepted", resp.Data.Accepted,
    "to", req.To,
)
```

### Reuse the Client

Create a single client instance and reuse it across requests:

```go theme={null}
// Good: Create once, reuse
var emailClient = lettr.NewClient(os.Getenv("LETTR_API_KEY"))

func sendEmail(to, subject, html string) error {
    resp, err := emailClient.Emails.Send(context.Background(), &lettr.SendEmailRequest{
        From:    "notifications@yourdomain.com",
        To:      []string{to},
        Subject: subject,
        Html:    html,
    })
    // ...
}
```

```go theme={null}
// Bad: Creating new client for each request
func sendEmail(to, subject, html string) error {
    client := lettr.NewClient(os.Getenv("LETTR_API_KEY")) // Don't do this
    // ...
}
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="Unverified domain error">
    If you see "The from address domain is not verified":

    * Verify your domain in the [Lettr dashboard](https://app.lettr.com/domains)
    * Ensure the `from` address uses the verified domain
    * Wait for DNS propagation (can take up to 48 hours)
    * See [Domain Verification](/knowledge-base/troubleshooting/domain-verification) for help
  </Accordion>

  <Accordion title="Authentication failed (401 error)">
    If you see authentication errors:

    * Check your API key is correct and starts with `lttr_`
    * Verify the key is 68 characters total (prefix + 64 hex chars)
    * Ensure the key hasn't been revoked in the [dashboard](https://app.lettr.com/api-keys)
    * Confirm you're reading from the correct environment variable
  </Accordion>

  <Accordion title="Context deadline exceeded">
    If requests timeout:

    * Increase the context timeout (default 10 seconds may be too short)
    * Check your network connectivity and firewall settings
    * Verify `app.lettr.com` is reachable: `ping app.lettr.com`
    * Consider using a custom HTTP client with longer timeouts
  </Accordion>

  <Accordion title="Module or import errors">
    If you see "module not found" or import errors:

    * Run `go mod tidy` to sync dependencies
    * Verify the import path: `github.com/lettr-com/lettr-go`
    * Check your Go version is 1.21 or later: `go version`
    * Clear module cache: `go clean -modcache`
  </Accordion>

  <Accordion title="Rate limit exceeded (429 error)">
    If you're hitting rate limits:

    * Implement exponential backoff retry logic (see Best Practices)
    * Use batch sending with controlled concurrency
    * Consider upgrading your Lettr plan for higher limits
    * Spread requests over time instead of bursts
  </Accordion>
</AccordionGroup>

## What's Next

<CardGroup cols={2}>
  <Card title="API Reference" icon="book" href="/api-reference/introduction">
    Complete API documentation
  </Card>

  <Card title="Templates" icon="file-code" href="/learn/templates/introduction">
    Use Lettr-managed templates
  </Card>

  <Card title="Webhooks" icon="webhook" href="/learn/webhooks/introduction">
    Track delivery events
  </Card>

  <Card title="Best Practices" icon="shield-check" href="/knowledge-base/best-practices/deliverability">
    Email deliverability tips
  </Card>
</CardGroup>
