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

# Rust

> Official JetEmail Rust SDK for transactional email

# Rust SDK

The **[jetemail](https://crates.io/crates/jetemail)** crate is the official Rust SDK for JetEmail transactional email (MIT license, async via **reqwest** on **Tokio**, with an optional `blocking` feature for synchronous use).

Create a **transactional** API key in the dashboard: **Outbound** → **Keys** → Add API key. Use that key with the SDK.

## Installation

```bash theme={null}
cargo add jetemail
```

Or add to your `Cargo.toml`:

```toml theme={null}
[dependencies]
jetemail = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
```

TLS uses `native-tls` by default; enable the `rustls` feature instead with `jetemail = { version = "0.1", default-features = false, features = ["rustls"] }`.

Crate on crates.io: [jetemail](https://crates.io/crates/jetemail). Source on GitHub: [jetemail/jetemail-rust](https://github.com/jetemail/jetemail-rust).

## Quick start

```rust theme={null}
use jetemail::{CreateEmailOptions, JetEmail};

#[tokio::main]
async fn main() -> jetemail::Result<()> {
    let client = JetEmail::new("your-transactional-api-key");

    let email = CreateEmailOptions::new(
        "Your App <hello@yourdomain.com>",
        "user@example.com",
        "Welcome!",
    )
    .with_html("<h1>Welcome!</h1>");

    let response = client.emails.send(email).await?;
    println!("{}", response.id);

    Ok(())
}
```

Use a `from` address on a **verified domain** (see [Getting started](/docs/transactional-email/getting-started)).

## Creating a client

```rust theme={null}
use jetemail::{ConfigBuilder, JetEmail};

// API key only
let client = JetEmail::new("your-transactional-api-key");

// From the JETEMAIL_API_KEY environment variable (panics if unset)
let client = JetEmail::default();

// With your own reqwest client (timeouts, proxies, ...)
let client = JetEmail::with_client("your-transactional-api-key", reqwest::Client::new());

// Full configuration
let config = ConfigBuilder::new("your-transactional-api-key")
    .base_url("https://api.jetemail.com") // optional
    .user_agent("my-app/1.0")             // optional
    .client(reqwest::Client::new())       // optional
    .build();
let client = JetEmail::with_config(config);
```

`JetEmail` is `Clone` and exposes two services as public fields:

| Service | Field | Description |
| - | - | - |
| Emails | `client.emails` | Send a single email |
| Batch | `client.batch` | Send up to 100 emails in one request |

## `client.emails.send(email)`

Send a single email. Build the request with `CreateEmailOptions::new(from, to, subject)` and chain `with_*` methods for optional fields. All fields are also public on the struct.

| Field | Required | Builder | Description |
| - | - | - | - |
| `from` | ✓ | `new(...)` | Sender, e.g. `"Name <email@domain.com>"` |
| `to` | ✓ | `new(...)` | Recipient(s): `&str`, `String`, `Vec<String>`, `&[&str]` or `[&str; N]` (max 50) |
| `subject` | ✓ | `new(...)` | Subject line |
| `html` | \* | `.with_html(..)` | HTML body (need `html` and/or `text`) |
| `text` | \* | `.with_text(..)` | Plain text (need `html` and/or `text`) |
| `cc`, `bcc`, `reply_to` | | `.with_cc(..)`, `.with_bcc(..)`, `.with_reply_to(..)` | Recipients, same types as `to` (max 50 each) |
| `headers` | | `.with_header(name, value)` | Custom headers; call once per header (`HashMap<String, String>`) |
| `attachments` | | `.with_attachment(Attachment)` | Attachments; call once per file (max 40MB total) |

```rust theme={null}
use jetemail::{Attachment, CreateEmailOptions};

let email = CreateEmailOptions::new(
    "App <noreply@example.com>",
    "user@example.com",
    "Order Confirmation",
)
.with_html("<p>Your order has been confirmed.</p>")
.with_text("Your order has been confirmed.")
.with_cc("manager@example.com")
.with_bcc(["archive@example.com"])
.with_reply_to("support@example.com")
.with_header("X-Custom-Header", "value")
.with_attachment(Attachment::from_path("/path/to/receipt.pdf")?);

let response = client.emails.send(email).await?;
println!("{}", response.id); // Message ID
```

### Responses

`send` returns `jetemail::Result<CreateEmailResponse>`, where `CreateEmailResponse { id: String }`.

## `client.batch.send(emails)`

Send up to **100** emails in one request as a `Vec<CreateEmailOptions>`. The SDK rejects an empty vec or more than 100 emails client-side (returned as `Error::JetEmail` with `status_code: 0`). Returns `BatchEmailResponse { data: Vec<CreateEmailResponse> }`.

<Warning>The REST batch endpoint returns `summary` and `results`, not `data`, so the current crate version fails to parse a successful batch response and returns `Error::Parse`. Until the crate is updated, send emails individually with `client.emails.send` or call the [batch endpoint](/docs/api-reference/introduction) directly.</Warning>

```rust theme={null}
use jetemail::CreateEmailOptions;

let emails = vec![
    CreateEmailOptions::new("you@example.com", "alice@example.com", "Hello Alice")
        .with_html("<p>Hi Alice!</p>"),
    CreateEmailOptions::new("you@example.com", "bob@example.com", "Hello Bob")
        .with_html("<p>Hi Bob!</p>"),
];

let response = client.batch.send(emails).await?;
for email in &response.data {
    println!("{}", email.id);
}
```

## Attachments

`Attachment::from_content(bytes, filename)` base64-encodes in-memory bytes; `Attachment::from_path(path)` reads a file synchronously (returning `std::io::Result<Attachment>`) and uses the file name as the filename. Attachments are sent as `{ "filename": ..., "data": <base64> }`.

<Note>`jetemail::Error` does not implement `From<std::io::Error>`, so using `?` on `from_path` requires a function that returns `Result<_, Box<dyn std::error::Error>>` (or map the error yourself).</Note>

```rust theme={null}
use jetemail::{Attachment, CreateEmailOptions};

let email = CreateEmailOptions::new(
    "you@example.com",
    "recipient@example.com",
    "Invoice",
)
.with_html("<p>Please find your invoice attached.</p>")
.with_attachment(Attachment::from_path("/path/to/invoice.pdf")?)
.with_attachment(Attachment::from_content(b"Hello World", "hello.txt"));

let response = client.emails.send(email).await?;
```

## Error handling

All methods return `jetemail::Result<T>` (an alias for `Result<T, jetemail::Error>`). The `Error` enum has three variants:

| Variant | When |
| - | - |
| `Error::JetEmail { message, status_code, response }` | API returned a non-2xx status (`status_code: u16`, `response: Option<serde_json::Value>` with the raw error body) |
| `Error::Http(reqwest::Error)` | Network/transport failure |
| `Error::Parse(String)` | Response could not be deserialized |

```rust theme={null}
use jetemail::Error;

match client.emails.send(email).await {
    Ok(response) => println!("Sent: {}", response.id),
    Err(Error::JetEmail { status_code, message, .. }) => {
        eprintln!("API error {}: {}", status_code, message);
    }
    Err(Error::Http(e)) => eprintln!("Network error: {}", e),
    Err(Error::Parse(e)) => eprintln!("Parse error: {}", e),
}
```

Common status codes: **400** invalid request, **401** bad/missing API key, **500** server error.

## Blocking usage

Enable the `blocking` feature to use the same API synchronously (no Tokio required). `JetEmail::with_client` and `ConfigBuilder::client` then take a `reqwest::blocking::Client`.

```toml theme={null}
[dependencies]
jetemail = { version = "0.1", features = ["blocking"] }
```

```rust theme={null}
let client = JetEmail::new("your-transactional-api-key");
let response = client.emails.send(email)?;
```

***

For additional examples, see the **[README on GitHub](https://github.com/jetemail/jetemail-rust)**. REST details: [API reference](/docs/api-reference/introduction).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.