The official Rust crate for the Sendry email API.
cargo add sendryOr add to Cargo.toml:
[dependencies]
sendry = "0.1"
tokio = { version = "1", features = ["full"] }Requires Rust 1.75+. Async only — built on reqwest + tokio.
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(())
}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()?;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, deleteuse 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.
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.
| 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"] }MIT — see LICENSE.