flowCreate.solutions

Notifications (Email + SMS)

This document defines generic engineering standards for sending emails and SMS notifications in backend services.

Scope:

  • Transactional email (support + system notifications)
  • Transactional SMS
  • Event-specific “notification processes” (schemas + APIs + orchestration)
  • Security controls for externally-triggered notification endpoints
  • Testing patterns for notification flows

Module layout (standard pattern)

notifications/
├── config.py                # from-address configuration via BaseSettings (or equivalent)
├── email_sender.py          # async email sending + templates + rate limiting + attachments
├── sms_sender.py            # async SMS sending (provider HTTP API) + optional templates
├── templates/               # message templates (HTML and/or plain text)
└── processes/               # event-specific processes + APIs + schemas
    ├── <process_name>/
    │   ├── apis.py
    │   ├── process.py
    │   └── schemas.py
    └── ...

Email standards

Template rendering

  • Store templates under notifications/templates/.
  • Render templates via Jinja2 using autoescape=True for HTML email bodies.
  • Prefer template context over string concatenation so templates stay consistent and testable.

Sender identity (“from” address)

  • Support at least two categories:
    • support: user-facing support communications
    • notification: system/operations notifications
  • Select the from-address based on the category, with environment-based overrides.

Delivery function contract

Implement a single async entry point (e.g. send_email(...)) that supports:

  • to_email (single recipient)
  • subject
  • template_name + template_context
  • is_html (HTML vs text)
  • type (support vs notification)
  • attachments (optional file paths or file-like payloads)
  • bcc_emails (optional list)

Attachments + BCC

  • Attachments must be encoded into the provider payload (commonly base64 for JSON APIs).
  • BCC should be supported for auditing/ops workflows.

Provider rate limiting + batch sends

  • Apply a provider-safe send rate limiter inside the sender module (e.g. lock + timestamps).
  • Provide a send_batch_emails(...) helper for multi-recipient notifications.
  • If per-recipient personalization is required, send individualized emails with rate limiting (don’t break provider limits).

SMS standards

Delivery + templates

  • Send SMS via an async HTTP client (e.g. httpx.AsyncClient) to your SMS provider API.
  • Support either:
    • direct message, or
    • template_name + template_context rendered into an SMS body.
  • If you use template rendering for SMS, disable HTML autoescaping for text bodies (autoescape=False) and keep templates text-safe.

Authentication + configuration

  • Read provider credentials and sender identity from environment/config.
  • Validate credentials on send and fail with a clear error when missing.

Notification “processes” (event-specific flows)

Use notifications/processes/<process_name>/ to keep notification flows predictable:

  • schemas.py
    • Validate request bodies with strict constraints (max lengths, required fields).
    • Sanitize only fields that intentionally accept HTML (rich text). For normal text fields, use validation + max lengths (do not mutate inputs).
  • apis.py
    • Define the FastAPI router.
    • Apply rate limiting per endpoint.
    • Enforce authentication/authorization appropriate to the endpoint (JWT token, API key, etc.).
    • Keep orchestration minimal: validate → call process → return response.
  • process.py
    • Resolve recipients (often from persisted configuration).
    • Build subject + template context.
    • Call the sender (send_email, send_batch_emails, send_sms).
    • Return a structured result: successes, failures, counts, and actionable messages.

Security standards for externally-triggered notification endpoints

For endpoints hit by external clients (widgets, integrations, CI/reporting):

  • Fail closed when auth configuration is missing.
  • Use Bearer token patterns (JWT or API key) and validate ownership/scope (e.g., token subject matches the target resource id).
  • Add an OPTIONS handler when the endpoint is called cross-origin.
  • Apply strict rate limiting (document the limit per endpoint).

Error notifications (exception handlers)

Support an optional “email admin” / “notify ops” behavior:

  • Exception handlers may trigger an admin notification email when an error is marked as “notify”.
  • Capture minimal, high-signal debug context (request path/method, selected headers, request body where safe).
  • Ensure the notification path is best-effort: log failures but don’t crash the main request path.

Testing standards for notifications

  • Endpoint tests:
    • Use an ASGI-bound httpx.AsyncClient (ASGITransport(app=app)) so tests do not use real network.
    • Patch the process function called by the endpoint and assert it is invoked for valid requests.
    • Cover auth failure cases (missing header, invalid token).
  • Process tests:
    • Unit-test template context mapping by patching send_email / send_sms with AsyncMock.
    • Assert the process passes the expected template_name, subject, and critical context fields.