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"] }
| Feature | Default | Description |
|---|---|---|
native-tls | yes | Use the system TLS implementation (OpenSSL on Linux, Secure Transport on macOS, SChannel on Windows) |
rustls | no | Use pure-Rust TLS via rustls (no system deps) |
Source
github.com/sendry-dev/sendry-rust — Apache-2.0 licensed.