> ## 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 Rust SDK patterns: CC and BCC, reply-to, attachments, templates, batch sending, and production error handling.

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

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

## Advanced Features

### CC and BCC Recipients

Add CC and BCC recipients:

```rust theme={null}
use lettr::{Lettr, CreateEmailOptions};

let email = CreateEmailOptions::new(
    "notifications@yourdomain.com",
    ["primary@example.com"],
    "Team notification",
)
.with_cc(["cc@example.com", "cc2@example.com"])
.with_bcc(["bcc@example.com"])
.with_html("<p>This email has CC and BCC recipients.</p>");

client.emails.send(email).await?;
```

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

```rust theme={null}
let email = CreateEmailOptions::new(
    "notifications@yourdomain.com",
    ["user@example.com"],
    "Support notification",
)
.with_reply_to("support@yourdomain.com")
.with_html("<p>Click reply to contact our support team.</p>");

client.emails.send(email).await?;
```

### Attachments

Add file attachments to your emails:

```rust theme={null}
use lettr::{Lettr, CreateEmailOptions, Attachment};
use std::fs;

// Read file and encode to base64
let file_data = fs::read("invoice.pdf")?;
let encoded = base64::encode(&file_data);

let email = CreateEmailOptions::new(
    "billing@yourdomain.com",
    ["customer@example.com"],
    "Your invoice",
)
.with_html("<p>Please find your invoice attached.</p>")
.with_attachments(vec![
    Attachment::new("invoice.pdf", "application/pdf", encoded),
]);

client.emails.send(email).await?;
```

Attach multiple files:

```rust theme={null}
use base64::{Engine as _, engine::general_purpose};

let pdf_data = fs::read("document.pdf")?;
let img_data = fs::read("logo.png")?;

let attachments = vec![
    Attachment::new(
        "document.pdf",
        "application/pdf",
        general_purpose::STANDARD.encode(&pdf_data),
    ),
    Attachment::new(
        "logo.png",
        "image/png",
        general_purpose::STANDARD.encode(&img_data),
    ),
];

let email = CreateEmailOptions::new(
    "sender@yourdomain.com",
    ["recipient@example.com"],
    "Files attached",
)
.with_html("<p>See attached files.</p>")
.with_attachments(attachments);

client.emails.send(email).await?;
```

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

```rust theme={null}
use std::collections::HashMap;

let mut substitution_data = HashMap::new();
substitution_data.insert("name".to_string(), "John Doe".to_string());
substitution_data.insert("company".to_string(), "Acme Corp".to_string());
substitution_data.insert("verify_url".to_string(), "https://example.com/verify/abc123".to_string());
substitution_data.insert("support_email".to_string(), "support@yourdomain.com".to_string());

let email = CreateEmailOptions::new_with_template(
    "notifications@yourdomain.com",
    ["user@example.com"],
    "welcome-email",
)
.with_substitution_data(substitution_data);

client.emails.send(email).await?;
```

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

```rust theme={null}
use std::collections::HashMap;

let mut headers = HashMap::new();
headers.insert("X-Campaign-ID".to_string(), "summer-2024".to_string());
headers.insert("X-Priority".to_string(), "high".to_string());
headers.insert("X-Mailer".to_string(), "Acme Mailer v1.0".to_string());

let email = CreateEmailOptions::new(
    "notifications@yourdomain.com",
    ["user@example.com"],
    "Custom headers example",
)
.with_html("<p>This email has custom headers.</p>")
.with_headers(headers);

client.emails.send(email).await?;
```

### Tracking

Enable open and click tracking:

```rust theme={null}
let email = CreateEmailOptions::new(
    "marketing@yourdomain.com",
    ["user@example.com"],
    "Newsletter",
)
.with_html("<p>Check out <a href='https://example.com'>our website</a>!</p>")
.with_click_tracking(true)
.with_open_tracking(true);

client.emails.send(email).await?;
```

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

```rust theme={null}
use std::collections::HashMap;

let mut metadata = HashMap::new();
metadata.insert("user_id".to_string(), "12345".to_string());
metadata.insert("order_id".to_string(), "ORD-98765".to_string());
metadata.insert("campaign".to_string(), "abandoned-cart".to_string());
metadata.insert("environment".to_string(), "production".to_string());

let email = CreateEmailOptions::new(
    "notifications@yourdomain.com",
    ["user@example.com"],
    "Order confirmation",
)
.with_html("<p>Your order has been confirmed.</p>")
.with_metadata(metadata);

client.emails.send(email).await?;
```

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

## Error Handling

The SDK provides idiomatic Rust error handling with the `Result` type:

```rust theme={null}
use lettr::{Lettr, CreateEmailOptions, Error};

let email = CreateEmailOptions::new(
    "invalid@unverified-domain.com",
    ["user@example.com"],
    "Test",
)
.with_html("<p>Test</p>");

match client.emails.send(email).await {
    Ok(response) => {
        println!("Email sent: {}", response.request_id);
    }
    Err(Error::Validation(err)) => {
        // 422 validation errors
        eprintln!("Validation failed: {}", err.message);
        for (field, messages) in err.errors {
            eprintln!("  {}: {:?}", field, messages);
        }
    }
    Err(Error::Authentication(err)) => {
        // 401 authentication errors
        eprintln!("Authentication failed: {}", err.message);
    }
    Err(Error::RateLimit(err)) => {
        // 429 rate limit errors
        eprintln!("Rate limit exceeded: {}", err.message);
    }
    Err(Error::Api(err)) => {
        // Other API errors (500, 503, etc.)
        eprintln!("API error: {}", err.message);
    }
    Err(err) => {
        // Network or other errors
        eprintln!("Request failed: {}", err);
    }
}
```

### Common Error Scenarios

**Unverified domain:**

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

**Invalid API key:**

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

**Rate limit exceeded:**

* Error: RateLimit error
* Message: "Too many requests"
* Solution: Implement exponential backoff retry logic

## Batch Sending

Send multiple emails concurrently using Tokio's join patterns:

```rust theme={null}
use lettr::{Lettr, CreateEmailOptions};
use tokio;

#[tokio::main]
async fn main() -> lettr::Result<()> {
    let client = Lettr::from_env();

    let recipients = vec![
        "user1@example.com",
        "user2@example.com",
        "user3@example.com",
    ];

    let mut tasks = vec![];

    for recipient in recipients {
        let client = client.clone();
        let task = tokio::spawn(async move {
            let email = CreateEmailOptions::new(
                "notifications@yourdomain.com",
                [recipient],
                "Batch notification",
            )
            .with_html("<p>This is a batch email.</p>");

            match client.emails.send(email).await {
                Ok(response) => println!("Sent to {}: {}", recipient, response.request_id),
                Err(e) => eprintln!("Failed to send to {}: {}", recipient, e),
            }
        });

        tasks.push(task);
    }

    // Wait for all tasks to complete
    for task in tasks {
        task.await?;
    }

    println!("All emails sent");

    Ok(())
}
```

### With Concurrency Limiting

Use a semaphore to limit concurrent requests:

```rust theme={null}
use tokio::sync::Semaphore;
use std::sync::Arc;

async fn send_batch(
    client: &Lettr,
    recipients: Vec<&str>,
    max_concurrent: usize,
) -> lettr::Result<()> {
    let semaphore = Arc::new(Semaphore::new(max_concurrent));
    let mut tasks = vec![];

    for recipient in recipients {
        let client = client.clone();
        let permit = semaphore.clone().acquire_owned().await?;

        let task = tokio::spawn(async move {
            let email = CreateEmailOptions::new(
                "notifications@yourdomain.com",
                [recipient],
                "Batch email",
            )
            .with_html("<p>Hello!</p>");

            let result = client.emails.send(email).await;
            drop(permit); // Release semaphore

            result
        });

        tasks.push(task);
    }

    for task in tasks {
        task.await??;
    }

    Ok(())
}

// Usage
let recipients = vec!["user1@example.com", "user2@example.com", "user3@example.com"];
send_batch(&client, recipients, 10).await?; // Max 10 concurrent requests
```

<Tip>
  For large batches, consider using a task queue or stream-based approach with `futures::stream::StreamExt` to manage concurrency and handle failures gracefully.
</Tip>

## Async Patterns

### Using with Tokio Runtime

The SDK is built on Tokio and works with any Tokio-based application:

```rust theme={null}
use lettr::Lettr;

#[tokio::main]
async fn main() -> lettr::Result<()> {
    let client = Lettr::from_env();
    // Your async code here
    Ok(())
}
```

### Using with Actix Web

Integrate with Actix Web handlers:

```rust theme={null}
use actix_web::{web, HttpResponse, Result};
use lettr::{Lettr, CreateEmailOptions};

async fn send_email(
    client: web::Data<Lettr>,
    recipient: web::Path<String>,
) -> Result<HttpResponse> {
    let email = CreateEmailOptions::new(
        "notifications@yourdomain.com",
        [recipient.as_str()],
        "Hello from Actix",
    )
    .with_html("<p>Hello!</p>");

    match client.emails.send(email).await {
        Ok(response) => Ok(HttpResponse::Ok().json(response)),
        Err(e) => Ok(HttpResponse::InternalServerError().body(e.to_string())),
    }
}
```

### Using with Axum

Integrate with Axum handlers:

```rust theme={null}
use axum::{extract::State, Json};
use lettr::{Lettr, CreateEmailOptions};

async fn send_email(
    State(client): State<Lettr>,
    recipient: String,
) -> Result<Json<String>, String> {
    let email = CreateEmailOptions::new(
        "notifications@yourdomain.com",
        [recipient.as_str()],
        "Hello from Axum",
    )
    .with_html("<p>Hello!</p>");

    match client.emails.send(email).await {
        Ok(response) => Ok(Json(response.request_id)),
        Err(e) => Err(e.to_string()),
    }
}
```

## Best Practices

### Use Environment Variables

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

```rust theme={null}
use std::env;

let api_key = env::var("LETTR_API_KEY")
    .expect("LETTR_API_KEY is required");

let client = Lettr::new(&api_key);
```

### Validate Before Sending

Validate email addresses before making API calls:

```rust theme={null}
use validator::validate_email;

fn is_valid_email(email: &str) -> bool {
    validate_email(email)
}

if !is_valid_email(&recipient) {
    eprintln!("Invalid email address: {}", recipient);
    return Err("Invalid email".into());
}
```

### Log Request IDs

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

```rust theme={null}
use log::info;

let response = client.emails.send(email).await?;
info!("Email sent successfully - Request ID: {}, Accepted: {}",
    response.request_id, response.accepted);
```

### Handle Errors Gracefully

Implement retry logic for transient errors:

```rust theme={null}
use tokio::time::{sleep, Duration};

async fn send_with_retry(
    client: &Lettr,
    email: CreateEmailOptions,
    max_retries: usize,
) -> lettr::Result<String> {
    for attempt in 0..=max_retries {
        match client.emails.send(email.clone()).await {
            Ok(response) => {
                println!("Email sent: {}", response.request_id);
                return Ok(response.request_id);
            }
            Err(Error::Validation(e)) => {
                // Don't retry validation errors
                return Err(Error::Validation(e));
            }
            Err(e) if attempt < max_retries => {
                let backoff = Duration::from_secs((attempt + 1) as u64);
                eprintln!("Attempt {} failed, retrying in {:?}: {}", attempt + 1, backoff, e);
                sleep(backoff).await;
            }
            Err(e) => return Err(e),
        }
    }

    unreachable!()
}
```

### Clone the Client

The `Lettr` client is cheap to clone and can be shared across tasks:

```rust theme={null}
let client = Lettr::from_env();

// Clone for use in spawned task
let client_clone = client.clone();
tokio::spawn(async move {
    // Use client_clone
});
```

### Use Structured Logging

Use the `tracing` crate for structured logging:

```rust theme={null}
use tracing::{info, error};

match client.emails.send(email).await {
    Ok(response) => {
        info!(
            request_id = %response.request_id,
            accepted = response.accepted,
            "Email sent successfully"
        );
    }
    Err(e) => {
        error!(error = %e, "Failed to send email");
    }
}
```

## 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="Request timeout">
    If requests timeout:

    * Increase the client timeout (default may be too short)
    * Check your network connectivity and firewall settings
    * Verify `app.lettr.com` is reachable
    * Use a custom reqwest client with longer timeout settings
  </Accordion>

  <Accordion title="Compilation or dependency errors">
    If you see compilation errors:

    * Run `cargo update` to update dependencies
    * Check your Rust version is 1.70 or later: `rustc --version`
    * Verify the crate name is correct: `lettr`
    * Clear build artifacts: `cargo clean`
  </Accordion>

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

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

  <Accordion title="Type or lifetime errors">
    If you encounter Rust-specific type errors:

    * Ensure your async runtime (Tokio) is properly configured
    * Check that you're using `.await` on async functions
    * Use `.clone()` when sharing the client across tasks
    * Verify all required fields are provided to the builder
  </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>
