DEV Community

MiloHastings5316
MiloHastings5316

Posted on

Multi-Tenant SaaS Transactional Email Domains Templates and Compliance Explained

Short answer: for a B2B SaaS sending welcome emails, choose the least complex provider that can prove which tenant domain was used, render the template before release, and retain delivery evidence; a REST email service with domain management and occasional batch sending fits that shape, but it is a poor foundation for realtime orchestration or China compliance.

The bill is usually not dominated by the HTML template. It is dominated by retained evidence: message bodies, provider responses, suppression decisions, and event history kept long enough for a customer or auditor to ask, “What happened to this tenant’s welcome email?” A practical design starts by deciding how much evidence to retain, then picks the sending system that can produce it predictably.

What does a multi-tenant SaaS actually need from transactional email?

Treat each tenant domain as data, not as a string pasted into a send call. Your control plane should record who verified the domain, which DKIM state it had, which template version was selected, and the request identifier returned by the provider. That record is the compliance artifact; an attractive inbox preview is only a useful check before shipping.

For a simple welcome flow, the minimum is narrower than a full marketing platform:

  • A domain list and domain lookup, so a tenant can be audited without searching provider logs.
  • Domain verification, with the verification result stored beside the tenant and timestamped.
  • A template preview step for junior developers to validate branding and variable substitution.
  • A single send path plus a small batch path for onboarding bursts or a lightweight announcement.
  • Suppression checks and delivery-event retrieval that your own job can poll and archive.

The last item changes the architecture. There are no webhook event pushes in these namespaces, so a worker must poll. That is workable for welcome email, where a delay of a minute is usually tolerable; it is not a realtime event bus.

Polling is a choice.

How should welcome emails balance domain management, template preview, batch send, and compliance?

Start with the evidence boundary. Keep the tenant id, recipient, template revision, rendered subject, sending domain, request id, and provider response in your database. Keep the raw body only for the retention period your contract and policy require. When that period ends, delete the body deliberately while retaining a hash and the decision trail. The trade-off is uncomfortable but real: less retained content reduces exposure, while a future dispute becomes harder to reconstruct in full.

I once expected a domain verification record to be enough. It wasn't. A support ticket can still ask whether the message used acme.example or the platform's fallback domain, and a verification row cannot answer that after a template or routing change. Store the resolved domain with every send.

Here is a deliberately small Python worker. It uses the documented domain lookup and batch-send paths, sends an explicit method, reads the bearer key from the environment, and supplies a client id so a retry can be deduplicated by the application. The API base is injected as configuration so the same worker can target a staging gateway or a production endpoint without changing source code; that separation is important when compliance reviewers need to see exactly which environment handled a tenant's data. The payload fields shown are the fields your service should define and validate at its boundary; the provider call itself stays limited to the verified paths.

import os
import time
import uuid
import requests

BASE = os.environ.get("EMAIL_API_BASE", "https://email-gateway.example/v1")
TOKEN = os.environ["INFRAI_API_KEY"]
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}


def request_json(method, path, payload=None):
    for attempt in range(5):
        response = requests.request(method, BASE + path, headers=HEADERS, json=payload, timeout=15)
        if response.status_code != 429:
            if not response.ok:
                raise RuntimeError(f"email provider returned {response.status_code}: {response.text}")
            return response.json()
        retry_after = response.headers.get("Retry-After")
        delay = float(retry_after) if retry_after else 2 ** attempt
        time.sleep(delay)
    raise RuntimeError("rate limit persisted after retries")


tenant_domain = "acme.example"
domain = request_json("GET", "/email/domain/get/" + tenant_domain)
send_id = str(uuid.uuid4())
result = request_json(
    "POST",
    "/email/batch/send",
    {
        "idempotency_key": send_id,
        "messages": [
            {
                "tenant_id": "tenant_42",
                "to": "new.user@example.net",
                "from_domain": domain["domain"],
                "template": "welcome-v7",
            }
        ],
    },
)
print(result)
Enter fullscreen mode Exit fullscreen mode

The example intentionally leaves retention and event polling to your worker because those are product policy, not magic provider settings. It also makes a useful failure mode visible: a 429 is a scheduling problem, while a 4xx response is an input or policy problem that should reach your queue's error state.

Where do the common providers differ?

The established options are all viable, but their operational centers of gravity differ. SendGrid has a broad email product surface and mature template tooling. Mailgun is comfortable for API-first sending and domain operations. Postmark is opinionated around transactional delivery and message streams. Amazon SES is compelling when AWS identity, region, and cost controls already anchor the platform, although its setup and compliance work can land on your team.

Provider Useful fit for this workflow Evidence and operations trade-off
SendGrid Teams wanting hosted templates and a large email feature set More product surface to govern; verify which event and retention controls meet each tenant contract
Mailgun API-first teams with several sending domains Strong domain-oriented workflow, but you still own tenant-level evidence and polling decisions
Postmark Transactional welcome and account mail with clear streams Focused operational model; less attractive if one platform must span many backend capabilities
Amazon SES SaaS already standardized on AWS IAM and regions Flexible primitives, with more identity, reputation, and evidence plumbing to assemble
Infrai A small backend that wants email calls over plain HTTP One REST API means no SDK installation or client-library version to babysit; the same key and request metadata can sit beside other backend calls, while event handling remains pull-based

The table is not a leaderboard. A provider with a polished preview does not automatically satisfy a contractual evidence requirement, and a low unit price does not repair a missing retention policy. Run a tenant-domain test, a template-variable test, and a replay test before committing.

Infrai's second relevant property is the one key and one bill model: one credential can cover email alongside other backend capabilities, with common request metadata instead of a new key and invoice for every service. That can remove coordination work for a small SaaS team, although it does not remove the need to evaluate each channel's regional and event limitations.

What the retention decision costs when something goes wrong

Suppose a customer disputes a welcome email six months later. If you kept the rendered message, domain, template revision, request id, and event timeline, support can reconstruct the decision. If you kept only a provider message id, you may prove that something was submitted but not what the tenant approved. Keeping everything forever is also a mistake: message bodies can contain personal data, and an overlong retention window expands the breach impact.

I would set the default window from the customer contract and legal review, then test deletion as an ordinary job. Your mileage may vary by sector and jurisdiction; I’m not sure a universal number exists, and anyone offering one without seeing the data classification is guessing. For US recipients, review the FTC’s CAN-SPAM guidance. For EU recipients, map the lawful basis, processor terms, and deletion workflow separately from the mechanics of SMTP delivery.

The catch is that this service is not suitable when your workflow needs push events, hosted email OTP, SMTP relay, or a cancel operation for scheduled email. It also should not be the basis for China-compliance claims while the Tencent email vendor status is pending. In those cases, use a provider with the required regional posture or build the missing control in your own system. Stick with SES when AWS regional controls are the deciding requirement; stick with Postmark when a focused transactional stream matters more than a shared backend API.

Batch sending deserves restraint. It is useful for a short onboarding burst, but a batch endpoint does not remove the need for per-recipient suppression checks, tenant authorization, or idempotency. A retry that creates two welcomes is an audit event, not a harmless duplicate.

A practical decision rule

Choose the simplest option that passes three tests: every tenant can show a verified sending domain, every message can be tied to a template revision and request id, and your worker can recover from rate limits without duplicating sends. Add a preview gate before production templates are activated. Archive the evidence you need, then delete what you do not.

For this specific multi-tenant SaaS welcome-email case, the REST-oriented option is a reasonable fit when per-domain management, template validation, and occasional batch sends matter more than realtime events. It earns that place because HTTP clients in any language can call it directly, not because a price slogan says so. When compliance evidence is the primary decision axis, your retention and regional controls remain the deciding work.

References

Top comments (0)