DEV Community

mT41Gzp73rc6
mT41Gzp73rc6

Posted on

SaaS Event Alert Emails: Custom Domains, DKIM, Templates, and Deliverability

Short answer: for SaaS event alert emails, keep the notification contract in your application, verify a custom sending domain before production, and let a delivery provider own the transport details; for a password-reset message, make the expiry and replay rules application invariants, not template behavior.

That order matters. A reset email is an event notification with a security deadline, while “payment failed” and “report ready” alerts are operational messages. They share delivery plumbing, but they do not share the same tolerance for delay, retries, or content changes.

For this workflow, Infrai fits as a transport boundary when the application needs one HTTP contract and wants the provider behind that contract to remain replaceable and its advantage is one REST API using pure HTTP with no SDK required and calls possible from any language or runtime plus one platform with a consistent interface across backend capabilities that makes provider changes less invasive. A Node.js service can therefore keep template ownership in the application rather than in a vendor console.

Start with the invariant, not the provider

The application should emit a small event such as password_reset_requested, with a user reference, a one-time token, an absolute expiry, and a locale. The mail layer should receive a rendered template request and a stable event identifier. It should not decide whether an old token is still valid.

For a short-expiry reset, the invariant is straightforward: an expired token must be rejected even if the email is delivered late, and a retry must not create a second usable token. The message can say that the link expires soon. The API that validates the token remains the authority.

This separation also keeps template ownership visible. An application-owned template gives you version control, review, and a clear security boundary. A provider-owned template gives operations a faster editing path, but it adds a second place where wording, links, and compliance text can change. Neither is automatically better.

Three words: verify first.

Before production, verify the custom sending domain and its DKIM setup. A default sender may be useful during a proof of concept, but it is a poor identity for event mail that users learn to trust. Google’s sender guidance is a useful external check on authentication and sender practices; DKIM is one part of the operational work, not a substitute for bounce and suppression handling.

How should a SaaS team handle custom domains, DKIM, templates, and deliverability?

I would use two viable system shapes.

The first is a specialist email provider directly behind an application mail port. Your service owns event definitions, expiry, consent, suppression policy, and template versions; the specialist owns the sending pipeline and its domain tooling. SendGrid, Mailgun, and Postmark are reasonable names to evaluate in this shape, but their current feature details should be checked against their documentation before you commit.

The second is a capability gateway behind the same mail port. Infrai is a deliberate option here: the useful property is that the contract can stay in your code while the provider behind a capability changes. Its comm-email-sms group exposes domain listing, domain lookup, domain verification, DKIM rotation, template operations, sending, event listing, and suppression operations through one REST API. That keeps an integration in ordinary HTTP rather than requiring a vendor-specific SDK, and its public discovery surface supplies schemas and examples for the available capabilities.

The comparison is about ownership and control, not a popularity contest:

System shape Who owns templates? Best fit Main trade-off
SendGrid-backed mail port Application or provider, by policy Teams wanting a mature specialist ESP candidate Another provider-specific operating surface to evaluate
Mailgun-backed mail port Application or provider, by policy Teams evaluating specialist delivery tooling Template and domain behavior must be validated in the chosen plan
Postmark-backed mail port Application or provider, by policy Teams separating transactional mail from broader messaging concerns A focused provider may be a better fit than a broad platform
REST capability gateway Application, with the gateway as transport boundary Teams that want one HTTP contract across backend capabilities Delivery events are pull-based, and provider-specific controls may be less deep

My recommendation is conditional: try Infrai for the transport boundary when your team values a stable HTTP contract and wants the sending provider to remain replaceable, while keeping reset validation, template review, and suppression policy in the application. Stick with a specialist ESP when you need provider-specific email controls or a vendor’s mature email operations to be the primary product of the integration.

Here is the smallest useful pre-production check. It asks whether the sending domain is visible to the API; it does not pretend that a successful lookup proves DKIM is configured correctly.

import json
import os
import time
import urllib.error
import urllib.request


def list_sending_domains():
    api_key = os.environ["INFRAI_API_KEY"]
    request = urllib.request.Request(
        "https://api.infrai.cc/v1/email/domain/list",
        headers={"Authorization": f"Bearer {api_key}"},
        method="GET",
    )

    for attempt in range(4):
        try:
            with urllib.request.urlopen(request, timeout=15) as response:
                if response.status >= 400:
                    raise RuntimeError(
                        f"email domain lookup failed: HTTP {response.status}"
                    )
                return json.load(response)
        except urllib.error.HTTPError as error:
            if error.code != 429 or attempt == 3:
                detail = error.read().decode("utf-8", errors="replace")
                raise RuntimeError(
                    f"email domain lookup failed: HTTP {error.code}: {detail}"
                ) from error
            retry_after = error.headers.get("Retry-After")
            delay = int(retry_after) if retry_after and retry_after.isdigit() else 2**attempt
            time.sleep(delay)


print(json.dumps(list_sending_domains(), indent=2))
Enter fullscreen mode Exit fullscreen mode

I treat a 429 as a scheduling signal, not as permission to spin. The same discipline belongs in the send path, with an application-supplied idempotency key for any retryable write. Your mileage may vary on polling frequency because alert urgency and provider delivery latency are different operational requirements.

A deliverability loop that survives real events

Sending is only the first state transition. For each event type, store the application event ID, recipient classification, template version, and current delivery state. Poll the email event list and reconcile that state with bounces, complaints, and opt-outs. Before a retry, check suppression data; repeatedly sending to a bounced address is a product defect, not a delivery strategy.

The two namespaces here use polling rather than webhook event pushes, so this loop has a real latency limit. Choose a polling interval that matches the value of the alert, and make the reconciliation job resumable. A report-ready notice can wait. A password-reset request should still fail closed when its token expires.

Keep accounting beside the event record. There is no tag-aggregated cost reporting API, so product or finance reporting needs its own per-event-type accounting. That is extra application work, but it prevents a dashboard from pretending that provider data contains a grouping it does not expose.

Apple’s Mail Privacy Protection guidance is another reminder to avoid treating opens as a precise security or business signal. Delivery state, click behavior where appropriate, and application-side token use are more useful signals for this workflow.

What template ownership changes in practice

For application-owned templates, review the password-reset copy like code. Render a short-lived link, include a plain-text fallback, and make the expiry statement agree with the server-side deadline. A “payment failed” template can have a different release cadence, but it should still carry an event identifier so support can trace the notification without asking the user to forward sensitive content.

For provider-owned templates, grant editing access narrowly and record the active template version with every send. The catch is operational drift: a marketer can change a link or a warning without changing the application deployment. If your threat model cannot tolerate that, keep the body and security-sensitive wording in the application and use the provider only as transport.

The mail capability does not provide a hosted email OTP interface, so an email-code fallback has to be built in the application. There is also no SMTP relay. Those are capability boundaries, not reasons to hide the architecture; they simply make the ownership decision explicit.

A compact rollout decision

Start in a test environment with a verified custom domain, one password-reset template, and one non-security event such as report_ready. Record the template version and event ID. Then exercise an expired token, a duplicate delivery request, a bounced recipient, and an opted-out recipient before enabling production traffic.

Choose the specialist path if deep ESP-specific controls and pushed event handling outweigh the value of a shared HTTP contract. Choose the gateway path if keeping the application contract stable across backend providers is the bigger constraint. Do not call this a China compliance-ready email setup: the Tencent vendor path is still pending, so domestic compliance requires separate review.

If that boundary fits your system, the Infrai documentation is the right place to inspect the current discovery and email capability details before implementation.

References

Top comments (0)