📧 Email Sending
🎯 Overview
| Category | Functions | Purpose |
|---|---|---|
| ⚙️ Config | get_smtp_config |
Load SMTP settings from env |
| 📁 Templates | get_template_path, load_template |
Manage markdown templates |
| ✉️ Sending | send_email, send_batch_emails |
Core send functions |
| 🎁 Convenience | send_welcome_email, send_invitation_email, send_password_reset_email |
Pre-built templates |
🏗️ Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Email Sending Flow │
├─────────────────────────────────────────────────────────────────┤
│ 1. Load SMTP config from environment variables │
│ 2. Load markdown template from templates/ directory │
│ 3. Substitute template variables (user_name, etc.) │
│ 4. Convert markdown to HTML via markdown_merge │
│ 5. Send via SMTP (Azure, SendGrid, AWS SES, etc.) │
└─────────────────────────────────────────────────────────────────┘
📋 Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
SMTP_HOST |
✅ | - | SMTP server hostname |
SMTP_PORT |
❌ | 587 | SMTP server port |
SMTP_USER |
✅ | - | Auth username |
SMTP_PASSWORD |
✅ | - | Auth password |
SMTP_MAIL_FROM |
✅ | - | Sender email |
SMTP_STARTTLS |
❌ | True | Use STARTTLS |
SMTP_SSL |
❌ | False | Use SSL (disables TLS) |
⚙️ SMTP Configuration
| Function | Purpose |
|---|---|
get_smtp_config |
Load SMTP config from environment vars |
get_smtp_config
def get_smtp_config(
)->Dict[str, Any]:
Load SMTP configuration from environment variables.
📁 Template Management
Built-in Templates:
We ship 3 production-ready markdown templates: - welcome.md - New user onboarding - invitation.md - Invite users to tenant
- password_reset.md - Password recovery
Custom Templates:
You can provide your own templates by passing custom_template_path to any email function.
| Function | Purpose |
|---|---|
get_template_path |
Resolve path to template file (built-in or custom) |
load_template |
Read template content as string |
load_template
def load_template(
template_name:str, # Template basename (e.g., 'welcome')
custom_template_path:Optional[str | Path]=None, # Custom path overrides package templates
)->str:
Load markdown email template as string.
get_template_path
def get_template_path(
template_name:str, # Template basename (e.g., 'welcome', 'invitation')
custom_template_path:Optional[str | Path]=None, # Custom path overrides package templates
)->Path:
Get absolute path to email template file.
✉️ Email Sending
| Function | Purpose |
|---|---|
send_email |
Send single email with template |
send_batch_emails |
Send to multiple recipients |
send_email
def send_email(
to_email:str, # Recipient email address
to_name:str, # Recipient display name
subject:str, # Email subject line
template_name:str, # Template name: 'welcome', 'invitation', 'password_reset'
template_vars:Dict[str, str], # Variables to substitute in template
test:bool=False, # If True, prints email instead of sending
smtp_config:Optional[Dict[str, Any]]=None, # Custom SMTP config (defaults to env vars)
custom_template_path:Optional[str | Path]=None, # Custom template path
)->Dict[str, Any]:
Send single email using markdown template with variable substitution.
📦 Batch Sending
| Function | Purpose |
|---|---|
send_batch_emails |
Send personalized emails to multiple recipients |
send_batch_emails
def send_batch_emails(
recipients:List[Dict[str, str]], # List of dicts with 'email' and 'name' keys
subject:str, # Email subject line
template_name:str, # Template name: 'welcome', 'invitation', 'password_reset'
template_vars_list:List[Dict[str, str]], # List of variable dicts, one per recipient
test:bool=False, # If True, prints emails instead of sending
pause:float=0.2, # Seconds between emails (rate limiting)
smtp_config:Optional[Dict[str, Any]]=None, # Custom SMTP config
custom_template_path:Optional[str | Path]=None, # Custom template path
)->List[Dict[str, Any]]:
Send personalized emails to multiple recipients.
🎁 Convenience Functions
Pre-built functions for common email types. All support custom_template_path for using your own templates.
| Function | Template | Purpose |
|---|---|---|
send_welcome_email |
welcome.md |
New user onboarding |
send_invitation_email |
invitation.md |
Invite to tenant |
send_password_reset_email |
password_reset.md |
Password recovery |
send_welcome_email
def send_welcome_email(
to_email:str, # Recipient email address
to_name:str, # Recipient display name
user_name:str, # User's name for personalization
tenant_name:str, # Tenant/organization name
dashboard_url:str, # URL to user's dashboard
test:bool=False, # If True, prints email instead of sending
custom_template_path:Optional[str | Path]=None, # Custom welcome.md template path
)->Dict[str, Any]:
Send welcome email to new user. Template vars: {user_name}, {tenant_name}, {dashboard_url}, {to_email}
send_invitation_email
def send_invitation_email(
to_email:str, # Recipient email address
to_name:str, # Recipient display name
inviter_name:str, # Name of person sending invitation
tenant_name:str, # Tenant/organization name
invitation_url:str, # URL to accept invitation
test:bool=False, # If True, prints email instead of sending
custom_template_path:Optional[str | Path]=None, # Custom invitation.md template path
)->Dict[str, Any]:
Send invitation email. Template vars: {inviter_name}, {tenant_name}, {invitation_url}, {to_email}
send_password_reset_email
def send_password_reset_email(
to_email:str, # Recipient email address
to_name:str, # Recipient display name
user_name:str, # User's name for personalization
reset_url:str, # Secure password reset URL
test:bool=False, # If True, prints email instead of sending
custom_template_path:Optional[str | Path]=None, # Custom password_reset.md template path
)->Dict[str, Any]:
Send password reset email. Template vars: {user_name}, {reset_url}, {to_email}
📝 Template Customization
Built-in Templates
The package ships with 3 production-ready templates in fh_saas/templates/:
| Template | Variables |
|---|---|
welcome.md |
{user_name}, {tenant_name}, {dashboard_url}, {to_email} |
invitation.md |
{inviter_name}, {tenant_name}, {invitation_url}, {to_email} |
password_reset.md |
{user_name}, {reset_url}, {to_email} |
Creating Custom Templates
- Copy a built-in template as a starting point
- Modify the markdown and keep variable names in
{brackets} - Pass
custom_template_path='/path/to/template.md'to any function
Templates use markdown_merge format - standard markdown with {variable} placeholders.