Sendry Docs
SDKs

Rust SDK

Official Rust crate for the Sendry email API.

Installation

cargo add sendry

Or in Cargo.toml:

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

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

Initialization

use sendry::Sendry;

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

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

    Ok(())
}

Sending email

use sendry::{Sendry, SendEmail};

let client = Sendry::new(std::env::var("SENDRY_API_KEY")?);

let response = 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: {}", response.id);

Batch send

use sendry::{BatchEmail, BatchEmailItem};

let result = client.emails().send_batch(BatchEmail {
    from: "hello@yourdomain.com".into(),
    emails: vec![
        BatchEmailItem { to: "a@example.com".into(), subject: "Hi A".into(),
            html: Some("<p>A</p>".into()), ..Default::default() },
        BatchEmailItem { to: "b@example.com".into(), subject: "Hi B".into(),
            html: Some("<p>B</p>".into()), ..Default::default() },
    ],
}).await?;

println!("Sent: {} | Failed: {}", result.sent, result.failed.len());

Resources

client.emails()        // send, send_batch, get, list
client.domains()       // create, verify, list, delete
client.templates()     // create, update, get, list, delete
client.contacts()      // upsert, get, list, delete
client.audiences()     // create, list, delete
client.campaigns()     // create, send, get, list
client.webhooks()      // create, list, delete
client.analytics()     // overview, breakdown, cohort, benchmark
client.suppression()   // add, check, remove, list
client.api_keys()      // create, list, delete
client.team()          // invite, list, update_role, remove
client.billing()       // overview, create_checkout, create_portal

Error handling

All errors are variants of sendry::Error:

use sendry::Error;

match client.emails().send(req).await {
    Ok(resp) => println!("Sent: {}", resp.id),
    Err(Error::RateLimit { retry_after, .. }) => {
        tokio::time::sleep(retry_after.unwrap_or_default()).await;
        // retry
    }
    Err(Error::Validation { details, .. }) => {
        eprintln!("Validation: {:?}", details);
    }
    Err(Error::Authentication(msg)) => eprintln!("Auth: {}", msg),
    Err(Error::NotFound(msg)) => eprintln!("Not found: {}", msg),
    Err(Error::Api { status, code, message, .. }) => {
        eprintln!("API {} {}: {}", status, code, message);
    }
    Err(Error::Network(e)) => eprintln!("Network: {}", e),
}

Webhook signature verification

use sendry::verify_webhook_signature;

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

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

verify_webhook_signature uses constant-time HMAC-SHA256 comparison.

Feature flags

[dependencies]
sendry = { version = "0.1", features = ["rustls"] }
FeatureDefaultDescription
native-tlsyesUse the system TLS implementation (OpenSSL on Linux, Secure Transport on macOS, SChannel on Windows)
rustlsnoUse pure-Rust TLS via rustls (no system deps)

Source

github.com/sendry-dev/sendry-rust — Apache-2.0 licensed.

On this page