# Groundmail with Rust

Use the Rust SDK in web handlers, workers and background jobs, or any server-side Rust service where your API key stays secret. It's async and runs on Tokio.

## 1. Install the SDK

```bash
cargo add groundmail
cargo add tokio --features macros,rt-multi-thread
```

## 2. Configure your API key

```bash
export GROUNDMAIL_API_KEY=gm_your_key_here
```

## 3. Send an email

```rust
use groundmail::{Client, SendEmail};

#[tokio::main]
async fn main() -> Result<(), groundmail::Error> {
    let client = Client::from_env()?;

    let email = SendEmail::new("ops@yourdomain.com", "user@example.com", "Your receipt")
        .html("<p>Thanks for your purchase.</p>")
        .idempotency_key("receipt-order-1042");

    let sent = client.emails().send(&email).await?;
    println!("{} {}", sent.id, sent.status);
    Ok(())
}
```

Retrying with the same idempotency key returns the first result instead of sending twice. Set any other `POST /emails` field with `.field("tags", json!(["receipt"]))`.

## Handle errors

API errors keep their status, message and code:

```rust
match client.emails().send(&email).await {
    Ok(sent) => println!("{}", sent.id),
    Err(groundmail::Error::Api { status, message, code, .. }) => {
        eprintln!("{status} {code:?}: {message}");
    }
    Err(other) => eprintln!("{other}"),
}
```

## Verify webhooks

Pass the raw request body, before parsing it, and the `Groundmail-Signature` header:

```rust
let valid = groundmail::verify_webhook_signature(&secret, &body, &signature_header);
```

## Other resources

The client also has `client.domains()`, `client.webhooks()`, `client.templates()` and `client.suppressions()`. Reach any other endpoint with `client.request(method, path, body)`.

## Resources

- [Crate on crates.io](https://crates.io/crates/groundmail)
- [Send over SMTP or REST without the SDK](https://docs.groundmail.dev/frameworks/rust)
- [Webhooks](https://docs.groundmail.dev/webhooks)
