- Module:
email
- Version:
v0.01
- Ownership default: If unclear, email owns until reassigned.
- Provides: outbound email delivery via SendGrid, template rendering, delivery tracking, and workflow triggers.
- Includes: simple and template emails, multi-recipient personalization, error handling, config management, database-triggered sends.
- Excludes: in-app messaging, push notifications, SMS (except 2FA emails coordinated with auth).
¶ 1. Roles and Views
[MOBILE] KMM Android/iOS app dev
[FE] Web frontend dev
[BE-RUST] Axum service dev
[BE-DB] PostgreSQL/Redis ops
- Simple send: text/HTML body.
- Template send: dynamic template data substitution.
- Recipients: TO/CC/BCC with per-recipient personalization.
- Workflows: DB-triggered sends (e.g., vendor interest, LOI, email verification).
- Tracking: message ID capture, optional webhooks (future).
- Config: SendGrid API key, sender identity, domain auth.
- Errors: normalized error mapping and logging.
- Service calls:
EmailService::send_simple(), EmailService::send_template().
- SeaORM hooks:
after_save triggers for defined events.
| Method |
Path |
Purpose |
Auth |
| POST |
/api/email/send |
Send simple email |
JWT |
| POST |
/api/email/template |
Send template email |
JWT |
| GET |
/api/email/templates |
List available templates |
JWT |
| GET |
/api/email/status/:id |
Check delivery status |
JWT |
{ "error": { "code":"STRING", "http":INT, "message":"STRING", "trace_id":"UUIDv4" } }
- Debounce submits; block duplicate sends in-flight.
- Validate inputs (email format); show backend errors verbatim where safe.
- Respect
429; exponential backoff with jitter.
- Do not store email contents containing PII locally after send.
| Key |
Required |
Example |
Notes |
SENDGRID_API_KEY |
yes |
SG.xxxxxx |
API key; never logged |
SENDER_EMAIL |
yes |
noreply@yourdomain.com |
Verified sender |
EMAIL_REPLY_TO |
no |
support@yourdomain.com |
Default reply-to |
EMAIL_BCC_ADMIN |
no |
admin@yourdomain.com |
Optional auditing |
EMAIL_DOMAIN_DKIM |
no |
true |
Domain auth improves deliverability |
RATE_RPS |
yes |
5 |
Per-IP or per-token, sensitive routes |
struct SimpleEmail { to: Vec<String>, from: String, subject: String,
content: EmailContent, reply_to: Option<String>, cc: Vec<String>, bcc: Vec<String> }
struct TemplateEmail { to: Vec<String>, from: String, template_id: String,
dynamic_template_data: std::collections::HashMap<String, String>,
reply_to: Option<String>, cc: Vec<String>, bcc: Vec<String> }
enum EmailContent { Text(String), Html(String), Mixed { text: String, html: String } }
api/ email service implementation; constants.rs for defaults.
- SendGrid REST integration with auth header; retry-safe for idempotent failures (future).
- Personalization payload per recipient; subject and substitutions supported.
- Normalized errors (
InvalidApiKey, NetworkError, InvalidEmailAddress, ConfigError, SendGridApiError, SerializationError).
- Respect SendGrid rate limits; surface
Retry-After to callers.
- Optional webhooks receiver for delivery events (future).
- TLS 1.3 to SendGrid.
- Secrets from env or secret manager; never log keys/headers.
- Sanitize template variables to prevent template injection.
- Strict email address validation before send.
- Log:
trace_id, template_id, count(to), latency, outcome, message_id on success.
- Metrics: requests/sec, success %, error % by type, p50/p95 latency.
- p95 send time ≤ 1500 ms per request to SendGrid.
- Batch up to provider limits; no local queue yet.
- No required tables for send operation itself.
- Tokens for verification originate in owning module (e.g.,
auth.email_verification).
- Optional: minimal
email_outbox table (future) for queueing/retries.
- Redis optional for lightweight rate limiting if endpoints are exposed.
- PII in emails kept to minimum; avoid secrets and one-time links beyond necessity.
- Redact PII in logs; store message IDs, not bodies.
- Domain authentication (SPF/DKIM) recommended; DMARC policy documented.
- Separate keys per environment; rotation policy documented.
- Availability: 99.9% for service layer.
- Scalability: handle ≥ 50 concurrent sends; degrade gracefully on provider throttling.
- Accessibility (FE): email-triggering UI meets WCAG 2.1 AA.
- Content builders (text/html/mixed) serialize correctly.
- Template substitution map encodes safely.
- Error mapping from SendGrid responses.
- SeaORM hooks trigger sends; failures do not affect commits.
- Rate-limit behavior returns
429 with Retry-After.
- Triggered emails sent once per action; UI debounces submits.
- User sees success/failure states; retries respect backoff.
- Client-side trigger calls documented; debounce/backoff implemented.
- UI actions to trigger sends; validation; user feedback states.
- No direct access to API key; uses backend endpoints only (when exposed).
email/api/service.rs, email/api/constants.rs, email/shared/error.rs.
- SendGrid client wrapper; logging/metrics; integration tests.
- Hook registrations; ops note on domain auth (SPF/DKIM/DMARC).
- Optional design for
email_outbox retry queue.
- Simple and template sends succeed with verified sender.
- DB-triggered workflows function and fail open.
- Errors normalized; logs contain trace_id and message_id.
- Rate limits respected; callers receive
429 with Retry-After when throttled.
- Secrets never logged; keys scoped per environment.
- Backward-compatible service API preferred.
- New public endpoints versioned under
/api/email/v1.
- Template IDs and variables documented; changes require release notes.
graph TD
A[Caller / DB Hook] --> B[EmailService]
B --> C[Build Payload]
C --> D[SendGrid API]
D -->|Message ID| E[Log+Metrics]
D -->|Error| F[Normalize Error -> Caller]
pub enum EmailError {
InvalidApiKey,
NetworkError,
InvalidEmailAddress,
ConfigError,
SendGridApiError(String),
SerializationError,
}