3 releases (breaking)
| new 0.3.0 | Apr 18, 2026 |
|---|---|
| 0.2.0 | Apr 17, 2026 |
| 0.1.0 | Feb 7, 2026 |
#127 in Email
130KB
2K
SLoC
lettr
Official Rust SDK for the Lettr Email API.
Send transactional emails with tracking, attachments, and template personalization.
Installation
Add lettr to your Cargo.toml:
[dependencies]
lettr = "0.1"
Or with the Cargo CLI:
cargo add lettr
Quick Start
use lettr::{Lettr, CreateEmailOptions};
#[tokio::main]
async fn main() -> lettr::Result<()> {
let client = Lettr::new("your-api-key");
let email = CreateEmailOptions::new(
"sender@example.com",
["recipient@example.com"],
"Hello from Lettr!",
)
.with_html("<h1>Welcome!</h1><p>Thanks for signing up.</p>")
.with_text("Welcome! Thanks for signing up.");
let response = client.emails.send(email).await?;
println!("Email sent! Request ID: {}", response.request_id);
Ok(())
}
Features
Send Emails
use lettr::{Lettr, CreateEmailOptions, Attachment};
# async fn run() -> lettr::Result<()> {
let client = Lettr::new("your-api-key");
// Simple email
let email = CreateEmailOptions::new("from@example.com", ["to@example.com"], "Hello!")
.with_html("<h1>Hello World!</h1>");
client.emails.send(email).await?;
// With all options
let email = CreateEmailOptions::new("from@example.com", ["to@example.com"], "Welcome!")
.with_from_name("Acme Inc")
.with_html("<h1>Hello {{first_name}}!</h1>")
.with_text("Hello {{first_name}}!")
.with_cc("cc@example.com")
.with_bcc("bcc@example.com")
.with_reply_to("support@example.com")
.with_reply_to_name("Support Team")
.with_tag("welcome-series")
.with_substitution("first_name", "John")
.with_metadata_entry("user_id", "12345")
.with_header("X-Custom-ID", "abc-123")
.with_click_tracking(true)
.with_open_tracking(true)
.with_transactional(true)
.with_inline_css(true)
.with_attachment(Attachment::new("invoice.pdf", "application/pdf", "base64data..."));
client.emails.send(email).await?;
# Ok(())
# }
Send with Templates
use lettr::{Lettr, CreateEmailOptions};
# async fn run() -> lettr::Result<()> {
let client = Lettr::new("your-api-key");
// Subject is optional when using a template
let email = CreateEmailOptions::new_with_template(
"from@example.com",
["to@example.com"],
"welcome-email",
)
.with_substitution("first_name", "John")
.with_substitution("company", "Acme Inc");
client.emails.send(email).await?;
# Ok(())
# }
Schedule Emails
use lettr::{Lettr, CreateEmailOptions};
use lettr::emails::ScheduleEmailOptions;
# async fn run() -> lettr::Result<()> {
let client = Lettr::new("your-api-key");
let email = CreateEmailOptions::new("from@example.com", ["to@example.com"], "Scheduled!")
.with_html("<h1>This was scheduled!</h1>");
// Must be at least 5 minutes in the future, within 3 days
let options = ScheduleEmailOptions::new(email, "2025-01-16T10:00:00Z");
let response = client.emails.schedule(options).await?;
println!("Scheduled! ID: {}", response.request_id);
// Get scheduled email details
let scheduled = client.emails.get_scheduled(&response.request_id).await?;
println!("State: {}, Scheduled at: {:?}", scheduled.state, scheduled.scheduled_at);
// Cancel a scheduled email
client.emails.cancel_scheduled(&response.request_id).await?;
# Ok(())
# }
List & Retrieve Emails
use lettr::Lettr;
use lettr::emails::{ListEmailsOptions, ListEmailEventsOptions};
# async fn run() -> lettr::Result<()> {
let client = Lettr::new("your-api-key");
// List recent emails
let options = ListEmailsOptions::new()
.per_page(10)
.from_date("2025-01-01");
let response = client.emails.list(options).await?;
for item in &response.events.data {
println!("{}: {:?} -> {:?}", item.event_id, item.friendly_from, item.rcpt_to);
}
// Get email details by request ID
let detail = client.emails.get("request-id", None, None).await?;
println!("State: {}, Recipients: {}", detail.state, detail.num_recipients);
for event in &detail.events {
println!(" {} at {}", event.event_type, event.timestamp);
}
// List email events with filters
let events = client.emails.list_events(
ListEmailEventsOptions::new()
.events(vec!["delivery".into(), "bounce".into()])
.per_page(25)
).await?;
for event in &events.events.data {
println!("{}: {} ({})", event.event_id, event.event_type, event.timestamp);
}
# Ok(())
# }
Manage Domains
use lettr::Lettr;
# async fn run() -> lettr::Result<()> {
let client = Lettr::new("your-api-key");
// List all domains
let domains = client.domains.list().await?;
for domain in &domains {
println!("{}: {} (can send: {})", domain.domain, domain.status, domain.can_send);
}
// Register a new domain
let result = client.domains.create("example.com").await?;
println!("DKIM selector: {:?}", result.dkim);
// Get domain details (includes DMARC, SPF, DNS provider info)
let detail = client.domains.get("example.com").await?;
println!("DKIM: {:?}, DMARC: {:?}, SPF: {:?}", detail.dkim_status, detail.dmarc_status, detail.spf_status);
// Verify domain DNS configuration
let verification = client.domains.verify("example.com").await?;
println!("DKIM: {}, CNAME: {}, DMARC: {}, SPF: {}",
verification.dkim_status, verification.cname_status,
verification.dmarc_status, verification.spf_status);
// Delete a domain
client.domains.delete("example.com").await?;
# Ok(())
# }
Webhooks
use lettr::Lettr;
use lettr::webhooks::{CreateWebhookOptions, UpdateWebhookOptions, WebhookAuthType, WebhookEventsMode};
# async fn run() -> lettr::Result<()> {
let client = Lettr::new("your-api-key");
// List webhooks
let webhooks = client.webhooks.list().await?;
for webhook in &webhooks {
println!("{}: {} (enabled: {})", webhook.id, webhook.url, webhook.enabled);
}
// Create a webhook
let options = CreateWebhookOptions::new(
"Order Notifications",
"https://example.com/webhook",
WebhookAuthType::Basic,
WebhookEventsMode::Selected,
)
.with_events(vec!["message.delivery".into(), "message.bounce".into()])
.with_basic_auth("user", "secret");
let webhook = client.webhooks.create(options).await?;
println!("Created webhook: {}", webhook.id);
// Update a webhook
let options = UpdateWebhookOptions::new()
.with_name("Updated Webhook")
.with_active(false);
client.webhooks.update(&webhook.id, options).await?;
// Delete a webhook
client.webhooks.delete(&webhook.id).await?;
# Ok(())
# }
Templates
use lettr::Lettr;
use lettr::templates::{ListTemplatesOptions, CreateTemplateOptions, UpdateTemplateOptions};
# async fn run() -> lettr::Result<()> {
let client = Lettr::new("your-api-key");
// List templates
let response = client.templates.list(ListTemplatesOptions::new()).await?;
for template in &response.templates {
println!("{}: {} (slug: {})", template.id, template.name, template.slug);
}
// Create a template
let options = CreateTemplateOptions::new("Welcome Email")
.with_html("<h1>Hello {{FIRST_NAME}}!</h1>")
.with_project_id(5);
let result = client.templates.create(options).await?;
println!("Created: {} (slug: {})", result.name, result.slug);
// Get template details
let detail = client.templates.get("welcome-email", None).await?;
println!("Version: {:?}, HTML: {:?}", detail.active_version, detail.html.is_some());
// Update a template
let options = UpdateTemplateOptions::new()
.with_name("Updated Welcome")
.with_html("<h1>Hello {{NAME}}!</h1>");
let updated = client.templates.update("welcome-email", options).await?;
println!("Updated version: {}", updated.active_version);
// Get merge tags
let tags = client.templates.get_merge_tags("welcome-email", None, None).await?;
for tag in &tags.merge_tags {
println!("{}: required={}", tag.key, tag.required);
}
// Get rendered HTML
let html = client.templates.get_html(5, "welcome-email").await?;
println!("HTML length: {}, Subject: {:?}", html.html.len(), html.subject);
// Delete a template
client.templates.delete("welcome-email", None).await?;
# Ok(())
# }
Projects
use lettr::Lettr;
use lettr::projects::ListProjectsOptions;
# async fn run() -> lettr::Result<()> {
let client = Lettr::new("your-api-key");
let response = client.projects.list(ListProjectsOptions::new()).await?;
for project in &response.projects {
println!("{}: {}", project.id, project.name);
}
# Ok(())
# }
Configuration
Environment Variable
// Reads from LETTR_API_KEY environment variable
let client = lettr::Lettr::from_env();
Feature Flags
| Feature | Default | Description |
|---|---|---|
native-tls |
Yes | Use the system's native TLS stack |
rustls-tls |
No | Use rustls for TLS |
blocking |
No | Enable synchronous (blocking) API |
Blocking API
Enable the blocking feature for synchronous usage:
[dependencies]
lettr = { version = "0.1", features = ["blocking"] }
use lettr::{Lettr, CreateEmailOptions};
fn main() -> lettr::Result<()> {
let client = Lettr::new("your-api-key");
let email = CreateEmailOptions::new("from@example.com", ["to@example.com"], "Hello!")
.with_text("Hello World!");
let response = client.emails.send(email)?;
println!("Request ID: {}", response.request_id);
Ok(())
}
Error Handling
The SDK uses a unified Error type that covers HTTP errors, API errors, and validation errors:
use lettr::{Lettr, CreateEmailOptions, Error};
# async fn run() {
let client = Lettr::new("your-api-key");
let email = CreateEmailOptions::new("from@example.com", ["to@example.com"], "Hello!")
.with_html("<h1>Hello!</h1>");
match client.emails.send(email).await {
Ok(response) => println!("Sent! ID: {}", response.request_id),
Err(Error::Validation(e)) => {
eprintln!("Validation failed: {}", e.message);
for (field, messages) in &e.errors {
eprintln!(" {}: {:?}", field, messages);
}
}
Err(Error::Api(e)) => eprintln!("API error: {} ({:?})", e.message, e.error_code),
Err(e) => eprintln!("Error: {e}"),
}
# }
API Coverage
| Endpoint | Method | Status |
|---|---|---|
/health |
GET |
client.health() |
/auth/check |
GET |
client.auth_check() |
/emails |
POST |
client.emails.send() |
/emails |
GET |
client.emails.list() |
/emails/{requestId} |
GET |
client.emails.get() |
/emails/events |
GET |
client.emails.list_events() |
/emails/scheduled |
POST |
client.emails.schedule() |
/emails/scheduled/{id} |
GET |
client.emails.get_scheduled() |
/emails/scheduled/{id} |
DELETE |
client.emails.cancel_scheduled() |
/domains |
GET |
client.domains.list() |
/domains |
POST |
client.domains.create() |
/domains/{domain} |
GET |
client.domains.get() |
/domains/{domain} |
DELETE |
client.domains.delete() |
/domains/{domain}/verify |
POST |
client.domains.verify() |
/webhooks |
GET |
client.webhooks.list() |
/webhooks |
POST |
client.webhooks.create() |
/webhooks/{id} |
GET |
client.webhooks.get() |
/webhooks/{id} |
PUT |
client.webhooks.update() |
/webhooks/{id} |
DELETE |
client.webhooks.delete() |
/templates |
GET |
client.templates.list() |
/templates |
POST |
client.templates.create() |
/templates/{slug} |
GET |
client.templates.get() |
/templates/{slug} |
PUT |
client.templates.update() |
/templates/{slug} |
DELETE |
client.templates.delete() |
/templates/{slug}/merge-tags |
GET |
client.templates.get_merge_tags() |
/templates/html |
GET |
client.templates.get_html() |
/projects |
GET |
client.projects.list() |
License
MIT
Dependencies
~4–11MB
~181K SLoC