> ## 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.

# Email client

> Full reference for the Paubox Rust SDK email client: building messages, sending email, tracking delivery, and error handling.

## Creating a client

```rust theme={null}
// From environment variables (recommended)
let client = PauboxClient::from_env()?;

// Direct constructor
let client = PauboxClient::new("YOUR_API_KEY", "YOUR_USERNAME");

// Builder (custom timeout, base URL for testing)
let client = PauboxClient::builder()
    .api_key("YOUR_API_KEY")
    .api_user("YOUR_USERNAME")
    .timeout(Duration::from_secs(15))
    .build()?;
```

See [Authentication](/rust-sdk/authentication) for details on all three options.

## Building a message

Use `Message::builder()` to compose a message. All methods take `impl Into<String>` or iterables:

```rust theme={null}
use paubox::Message;

let message = Message::builder()
    .from("sender@yourdomain.com")           // required
    .to(["alice@example.com"])               // required; accepts any iterable
    .subject("Your results are ready")       // required
    .text_content("Plain text body.")        // at least one of text/html required
    .html_content("<p>HTML body.</p>")       // optional
    .reply_to("support@yourdomain.com")      // optional
    .cc(["manager@example.com"])             // optional
    .bcc(["audit@example.com"])              // optional
    .allow_non_tls(false)                    // optional; default false
    .force_secure_notification(true)         // optional
    .build()?;
```

`.build()` validates required fields and returns `Err(PauboxError::Validation(...))` if any are missing.

### Attachments

The `from_bytes` constructor base64-encodes the data automatically — no manual encoding needed:

```rust theme={null}
use paubox::Attachment;
use std::fs;

let data = fs::read("report.pdf")?;

let attachment = Attachment::from_bytes("report.pdf", "application/pdf", &data);
```

If you already have base64-encoded content:

```rust theme={null}
let attachment = Attachment::from_base64("report.pdf", "application/pdf", encoded_string);
```

Add attachments to a message via the builder:

```rust theme={null}
let message = Message::builder()
    // ...other fields...
    .attachment(attachment)
    .build()?;
```

## Send a message

```rust theme={null}
let response = client.send_message(&message).await?;
println!("Tracking ID: {}", response.source_tracking_id);
```

`SendResponse` fields:

| Field                | Description                  |
| -------------------- | ---------------------------- |
| `source_tracking_id` | Use to check delivery status |
| `message`            | Status message from the API  |

## Check delivery status

```rust theme={null}
let disposition = client
    .get_email_disposition(&response.source_tracking_id)
    .await?;

for delivery in &disposition.message_deliveries {
    println!(
        "{} → {} (opened: {})",
        delivery.recipient,
        delivery.delivery_status,
        delivery.opened_status
    );
}
```

`MessageDelivery` fields:

| Field             | Description                           |
| ----------------- | ------------------------------------- |
| `recipient`       | Email address                         |
| `delivery_status` | e.g. `delivered`, `failed`, `pending` |
| `delivery_time`   | ISO 8601 timestamp, if delivered      |
| `opened_status`   | `opened` or `unopened`                |
| `opened_time`     | ISO 8601 timestamp, if opened         |

## Health check

```rust theme={null}
client.api_status().await?;
```

Returns `Ok(())` if the API is reachable, or a `PauboxError` if not.

## Error handling

All methods return `Result<T, PauboxError>`. Match on the variants to handle specific conditions:

```rust theme={null}
use paubox::PauboxError;

match client.send_message(&message).await {
    Ok(response) => println!("Sent: {}", response.source_tracking_id),
    Err(PauboxError::Auth(msg)) => eprintln!("Invalid credentials: {}", msg),
    Err(PauboxError::Http { status, body }) => {
        eprintln!("HTTP {}: {}", status, body);
    }
    Err(PauboxError::Validation(msg)) => eprintln!("Invalid message: {}", msg),
    Err(e) => eprintln!("Error: {}", e),
}
```

`PauboxError` variants:

| Variant                          | When it occurs                               |
| -------------------------------- | -------------------------------------------- |
| `Auth(String)`                   | HTTP 401 — invalid or missing credentials    |
| `Http { status, body }`          | Non-2xx response other than 401              |
| `Request(reqwest::Error)`        | Network or TLS failure                       |
| `Deserialize(serde_json::Error)` | Unexpected response format                   |
| `Validation(String)`             | Missing required message field               |
| `EnvVar(String)`                 | Missing environment variable in `from_env()` |
| `Url(url::ParseError)`           | URL construction failure                     |
