sendry 0.2.0

Official Rust crate for the Sendry email API
Documentation

Sendry Rust SDK

The official Rust crate for the Sendry email API.

Crates.io docs.rs License

Installation

cargo add sendry

Or add to Cargo.toml:

[dependencies]
sendry = "0.1"
tokio = { version = "1", features = ["full"] }

Requires Rust 1.75+. Async only — built on reqwest + tokio.

Quick start

use sendry::{Sendry, SendEmail};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Sendry::new(std::env::var("SENDRY_API_KEY")?);

    let resp = client.emails().send(SendEmail {
        from:    "hello@yourdomain.com".into(),
        to:      vec!["user@example.com".into()],
        subject: "Welcome".into(),
        html:    Some("<p>Thanks for signing up.</p>".into()),
        text:    Some("Thanks for signing up.".into()),
        ..Default::default()
    }).await?;

    println!("sent: {}", resp.id);
    Ok(())
}

Configuration

use std::time::Duration;

let client = Sendry::builder()
    .api_key(std::env::var("SENDRY_API_KEY")?)
    .base_url("https://api.sendry.online")
    .timeout(Duration::from_secs(30))
    .max_retries(2)
    .build()?;

Resources

client.emails()      // send, send_batch, get, list
client.domains()     // create, verify, get, list, delete
client.templates()   // create, get, list, delete
client.contacts()    // upsert, get, list, delete
client.audiences()   // create, get, list, delete
client.campaigns()   // create, get, list, send
client.webhooks()    // create, get, list, delete

Error handling

use sendry::Error;

match client.emails().send(req).await {
    Ok(resp) => println!("sent: {}", resp.id),
    Err(Error::Authentication(msg)) => eprintln!("auth: {msg}"),
    Err(Error::Validation { details, .. }) => eprintln!("validation: {details:?}"),
    Err(Error::RateLimit { retry_after, .. }) => {
        tokio::time::sleep(retry_after.unwrap_or_default()).await;
    }
    Err(Error::Api { status, message, .. }) => eprintln!("API {status}: {message}"),
    Err(Error::Network(e)) => eprintln!("network: {e}"),
    Err(e) => eprintln!("{e}"),
}

Error::is_retryable() returns true for Network, RateLimit, and Api errors with 5xx status.

Webhook signature verification

use sendry::verify_webhook_signature;

let valid = verify_webhook_signature(
    request_body.as_bytes(),
    request.headers().get("x-sendry-signature").unwrap().to_str()?,
    &webhook_secret,
);

if !valid {
    return Err(StatusCode::UNAUTHORIZED.into());
}

Constant-time comparison via the subtle crate.

Feature flags

Flag Default Description
native-tls yes Use the system TLS stack
rustls no Use pure-Rust TLS (no system deps)
[dependencies]
sendry = { version = "0.1", default-features = false, features = ["rustls"] }

License

MIT — see LICENSE.