📧 Email Sending

Send emails via SMTP using markdown templates with markdown_merge.

🎯 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

source

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

source

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.


source

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

source

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

source

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

source

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}


source

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}


source

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

  1. Copy a built-in template as a starting point
  2. Modify the markdown and keep variable names in {brackets}
  3. Pass custom_template_path='/path/to/template.md' to any function

Templates use markdown_merge format - standard markdown with {variable} placeholders.