Email Providers
FORGE supports sending transactional emails through multiple provider drivers. The email system handles account verification, password resets, notifications, and any custom email your application needs to send.
EmailProvider Trait
All email drivers implement the EmailProvider trait:
#[async_trait]
pub trait EmailProvider: Send + Sync {
/// Send a structured email message
async fn send(&self, message: EmailMessage) -> Result<EmailResult, EmailError>;
/// Send a templated email with dynamic data
async fn send_template(
&self,
to: &str,
template: &str,
data: &serde_json::Value,
) -> Result<EmailResult, EmailError>;
/// Validate an email address format and optionally check deliverability
async fn validate_email(&self, email: &str) -> Result<bool, EmailError>;
}EmailMessage Struct
The EmailMessage struct provides full control over email composition:
pub struct EmailMessage {
/// Recipient email address(es)
pub to: Vec<String>,
/// Carbon copy recipients
pub cc: Vec<String>,
/// Blind carbon copy recipients
pub bcc: Vec<String>,
/// Email subject line
pub subject: String,
/// Plain text body (fallback for HTML-incapable clients)
pub body_text: Option<String>,
/// HTML body content
pub body_html: Option<String>,
/// File attachments
pub attachments: Vec<EmailAttachment>,
/// Reply-to address (overrides the default from address)
pub reply_to: Option<String>,
}
pub struct EmailAttachment {
/// File name as it appears in the email
pub filename: String,
/// MIME type (e.g., "application/pdf")
pub content_type: String,
/// Raw file bytes
pub data: Vec<u8>,
}EmailResult Struct
Every send operation returns an EmailResult confirming delivery:
pub struct EmailResult {
/// Unique message identifier from the provider
pub message_id: String,
/// Delivery status
pub status: EmailStatus,
}
pub enum EmailStatus {
/// Message accepted by the provider for delivery
Queued,
/// Message delivered to the recipient's mail server
Delivered,
/// Delivery failed
Failed(String),
}Available Drivers
SMTP (Default)
Standard SMTP email delivery. This is the default driver and requires no additional installation. It works with any standard SMTP server including Gmail, Outlook, Amazon SES relay, and local development servers like Mailhog.
Configuration:
| Setting | Type | Default | Description |
|---|---|---|---|
email_host | string | -- | SMTP server hostname |
email_port | number | 587 | SMTP server port |
email_username | string | -- | SMTP authentication username |
email_password | encrypted | -- | SMTP authentication password |
email_encryption | string | "tls" | Encryption method: tls, ssl, or none |
email_from_address | string | -- | Default sender email address |
email_from_name | string | -- | Default sender display name |
TIP
SMTP is the default driver and works out of the box. For local development, use Mailhog with email_host: localhost, email_port: 1025, and email_encryption: none to capture all outgoing emails without actually delivering them.
Common SMTP ports
| Port | Protocol | Use Case |
|---|---|---|
25 | SMTP | Server-to-server relay (often blocked by ISPs) |
465 | SMTPS | Implicit SSL/TLS |
587 | SMTP | Submission with STARTTLS (recommended) |
1025 | SMTP | Mailhog / local development |
SendGrid
Cloud email delivery via the SendGrid API. SendGrid provides high-volume sending, delivery analytics, and template management through their dashboard.
Installation:
forge provider:add email:sendgridConfiguration:
| Setting | Type | Description |
|---|---|---|
sendgrid_api_key | encrypted | SendGrid API Key |
email_from_address | string | Verified sender email address |
email_from_name | string | Default sender display name |
Features:
- Transactional emails -- High-deliverability sending optimized for application-generated messages
- Template management -- Create and manage email templates in the SendGrid dashboard, then reference them by ID
- Delivery tracking -- Real-time delivery status, open rates, and click tracking
- Webhooks -- Receive callbacks for delivery events (delivered, bounced, opened, clicked)
WARNING
SendGrid requires sender verification. Ensure your email_from_address is verified in your SendGrid account before sending. Unverified senders will be rejected.
Mailgun
Email delivery via the Mailgun API.
Installation:
forge provider:add email:mailgunConfiguration:
| Setting | Type | Description |
|---|---|---|
mailgun_api_key | encrypted | Mailgun API Key |
mailgun_domain | string | Mailgun sending domain |
mailgun_region | string | API region: us or eu |
email_from_address | string | Default sender address |
email_from_name | string | Default sender display name |
Amazon SES
Email delivery via Amazon Simple Email Service.
Installation:
forge provider:add email:sesConfiguration:
| Setting | Type | Description |
|---|---|---|
ses_access_key | encrypted | AWS Access Key ID |
ses_secret_key | encrypted | AWS Secret Access Key |
ses_region | string | AWS region (e.g., us-east-1) |
email_from_address | string | Verified sender address |
email_from_name | string | Default sender display name |
WARNING
Amazon SES starts in sandbox mode, which only allows sending to verified email addresses. Request production access through the AWS console for unrestricted sending.
Usage
Creating the Provider
The factory creates the correct driver based on your configuration:
use crate::services::email::EmailFactory;
let email = EmailFactory::create(&settings).await?;Sending a Plain Email
let message = EmailMessage {
to: vec!["user@example.com".to_string()],
cc: vec![],
bcc: vec![],
subject: "Welcome to Our App".to_string(),
body_text: Some("Thanks for signing up!".to_string()),
body_html: Some("<h1>Welcome!</h1><p>Thanks for signing up.</p>".to_string()),
attachments: vec![],
reply_to: None,
};
let result = email.send(message).await?;
println!("Sent with message ID: {}", result.message_id);Sending with Attachments
let pdf_bytes = std::fs::read("invoice.pdf")?;
let message = EmailMessage {
to: vec!["billing@example.com".to_string()],
cc: vec!["accounting@example.com".to_string()],
bcc: vec![],
subject: "Invoice #1234".to_string(),
body_text: Some("Please find your invoice attached.".to_string()),
body_html: Some("<p>Please find your invoice attached.</p>".to_string()),
attachments: vec![
EmailAttachment {
filename: "invoice-1234.pdf".to_string(),
content_type: "application/pdf".to_string(),
data: pdf_bytes,
}
],
reply_to: Some("billing@mycompany.com".to_string()),
};
let result = email.send(message).await?;Sending a Templated Email
use serde_json::json;
let result = email.send_template(
"user@example.com",
"welcome",
&json!({
"name": "Jane Doe",
"app_name": "My Application",
"verification_url": "https://example.com/verify?token=abc123"
})
).await?;TIP
Template-based emails are recommended for all user-facing messages. They keep email content separate from application logic and allow non-developers to modify email copy through the admin panel or provider dashboard.
Validating an Email Address
let is_valid = email.validate_email("user@example.com").await?;
if !is_valid {
return Err(AppError::validation("Invalid email address"));
}Configuration via Admin
Email settings are managed through the Settings admin page under the Email group:
Settings > Email
┌──────────────────────────────────────────────────┐
│ Driver: [SMTP v] │
│ Host: [smtp.example.com ] │
│ Port: [587 ] │
│ Username: [noreply@example.com] │
│ Password: [................ ] │
│ Encryption: [TLS v] │
│ From Name: [My Application ] │
│ From Address: [noreply@example.com] │
└──────────────────────────────────────────────────┘Error Handling
All email operations return Result<EmailResult, EmailError> with structured error types:
match email.send(message).await {
Ok(result) => println!("Sent: {}", result.message_id),
Err(EmailError::InvalidAddress) => println!("Bad email address"),
Err(EmailError::ProviderError(msg)) => println!("Provider error: {}", msg),
Err(EmailError::TemplateNotFound(name)) => println!("Template '{}' not found", name),
Err(EmailError::AttachmentTooLarge(size)) => println!("Attachment exceeds limit: {} bytes", size),
Err(e) => println!("Unexpected error: {}", e),
}WARNING
Most email providers impose attachment size limits (typically 10-25 MB). For large files, consider uploading to Storage and including a download link in the email body instead.