```
## Email Queue (Background with Celery)
```python
from celery import Celery
celery = Celery("tasks", broker=settings.REDIS_URL)
@celery.task(bind=True, max_retries=3, default_retry_delay=60)
def send_email_task(self, to: str, subject: str, html: str):
try:
send_email_sync(to, subject, html)
except Exception as exc:
raise self.retry(exc=exc)
# Usage — don't block the request
send_email_task.delay(user.email, "Welcome!", welcome_html)
```
## SMTP (nodemailer / standard SMTP)
```typescript
import nodemailer from 'nodemailer'
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: 587,
secure: false,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
})
export async function sendEmail(to: string, subject: string, html: string) {
await transporter.sendMail({
from: '"MyApp" ',
to,
subject,
html,
})
}
```
## Rules
- Use a transactional email service (Resend, SendGrid, Mailgun) — NEVER raw SMTP in production
- Always queue emails (Celery/background task) — never send synchronously in request handler
- Verify sender domain with SPF, DKIM, DMARC records (deliverability)
- Never build HTML emails with `f-strings` with user input (XSS in email clients)
- Include unsubscribe link in marketing emails (CAN-SPAM / GDPR)
- Test email rendering with Litmus or Email on Acid before launch
- Rate limit: max 1 email per minute per recipient to avoid spam classification
- Use separate API keys for transactional vs marketing emails
- Log email send attempts (ID, recipient, template, status) in your DB