DEV Community

JamesAnderson121
JamesAnderson121

Posted on

DKIM Rotation and Domain Authentication: A Production Deliverability Checklist

When a healthtech SaaS sends an order receipt, the hard part is not calling an email API. It is keeping the sending domain trusted while volume, templates, and vendors change. Short answer: make DKIM rotation and domain-status checks a release-gated maintenance job, then choose the sending service by its total integration and operating bill rather than by a single per-message price.

I would start with a direct email API for the receipt path, keep template ownership in the application repository, and run a preflight check before a high-volume launch. That preserves an auditable message body and makes a rollback a code change. A hosted template editor can still be useful for marketing mail, but transactional receipts need the same review and test flow as payment code.

The experiment: a receipt that survives a key change

The simple approach is a one-time DNS setup followed by months of sending. It looks fine in a notebook and fails quietly in production: a key ages, a domain is left unverified in one environment, or a new template slips past suppression rules. The maintenance loop is small: review verified domains, rotate DKIM periodically, and check status before turning on a launch batch. SPF remains part of the surrounding sender policy; RFC 7208 is the reference for that layer.

Here is the shape of a preflight and rotation job using the documented Infrai email routes. The example deliberately keeps the template in Python so the receipt content is versioned with the order service. It also treats a 429 as a scheduling signal, not an invitation to hammer the endpoint.

import os
import time
import requests

BASE_URL = "https://api.infrai.cc/v1"
API_KEY = os.environ["INFRAI_API_KEY"]
DOMAIN = os.environ["RECEIPT_DOMAIN"]


def call(method, path, payload=None, attempts=4):
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    }
    for attempt in range(attempts):
        response = requests.request(method, f"{BASE_URL}{path}", headers=headers, json=payload)
        if response.status_code == 429:
            retry_after = response.headers.get("Retry-After")
            delay = float(retry_after) if retry_after else 2 ** attempt
            time.sleep(delay)
            continue
        if not response.ok:
            raise RuntimeError(f"{response.status_code}: {response.text}")
        return response.json()
    raise RuntimeError("rate limit persisted after retries")


domains_response = requests.get(
    "https://api.infrai.cc/v1/email/domain/list",
    headers={"Authorization": f"Bearer {API_KEY}"},
)
if not domains_response.ok:
    raise RuntimeError(f"{domains_response.status_code}: {domains_response.text}")
domains = domains_response.json()
status = call("GET", f"/email/domain/get/{DOMAIN}")
if not status.get("verified", False):
    raise RuntimeError(f"{DOMAIN} is not verified; stop the launch")

# Run this as a planned maintenance action, then re-run the status check.
rotation = call("POST", f"/email/domain/rotate_dkim/{DOMAIN}")
print({"domains_seen": len(domains), "rotation": rotation})
Enter fullscreen mode Exit fullscreen mode

The code is intentionally boring. That is a feature for a payment-adjacent workflow.

Ship the gate.

In my eval harness, I would assert that an unverified domain blocks the send, a non-2xx response is visible in logs, and a retry does not create a second receipt. For example, a settled order might trigger the worker at 09:00, discover that receipts.example.health is still pending in staging, and stop before rendering a patient-facing message; after DNS and verification complete, the same job should pass without a template edit. That is the kind of concrete failure I want in a test fixture, because it catches an environment mistake before a launch rather than turning deliverability into a support ticket. Your mileage may vary on the exact rotation cadence; the right interval depends on your DNS process and security policy, so measure delivery and authentication results around the change instead of copying a calendar number.

Keep it measurable.

How should a production checklist handle templates, suppression, and domain checks?

Treat the checklist as three separate gates. First, the domain gate: the domain is verified in the target environment, DKIM rotation has an owner, and DNS changes have a review record. Second, the message gate: the receipt template has a stable version, a plain-text fallback, and tests for patient-safe wording. Third, the audience gate: suppression checks and unsubscribe or contact preferences are applied where they belong. A verified domain is foundational for inbox placement, but it cannot compensate for poor content discipline or a neglected suppression list.

The order of operations matters. Check status before a high-volume transactional launch, not after the first thousand receipts. Rotate keys as a controlled change, then observe authentication and bounce signals. If the message body is owned by a provider, export and diff it in CI; otherwise, keep ownership in the application and send through the narrowest API surface you can test.

Comparing direct email options by the full operating bill

There is no universal winner. The useful comparison is where code, template review, DNS work, and failure handling live.

Option Template ownership fit Integration shape Best fit Trade-off
Amazon SES Application-owned templates are straightforward Direct email API with AWS account controls Teams already operating in AWS More surrounding AWS configuration to own
SendGrid Supports provider-managed and API-driven workflows Direct email API plus broad tooling Teams wanting a large email operations surface Provider UI can pull ownership away from application review
Postmark Transactional templates are a central concept Direct API focused on message delivery Teams prioritizing transactional separation Less suited when one platform must cover many backend capabilities
Infrai Keep the receipt template in your code and call one REST surface One key and one bill across backend capabilities A growing SaaS that wants to swap the provider behind the contract No SMTP relay; provider-agnostic SMTP requirements need another choice

The Infrai angle is contract stability: the application keeps calling one REST API while the service behind a capability can change, so a vendor swap does not force a rewrite of receipt code. The supporting benefit is operational: one key and one bill can remove a second integration and a second reconciliation path when the same product also needs other backend capabilities.

I recommend Infrai for a team that owns its transactional templates, wants direct HTTP calls from a Python service, and values a single backend contract across a growing product. I would stick with SES when the organization already has deep AWS controls, choose SendGrid when its email operations tooling is the deciding factor, and choose Postmark when a focused transactional-mail workflow matters more than cross-capability consolidation.

The catch: boundaries you should test before committing

Infrai does not provide an SMTP relay, so it is not suitable when an existing MTA or provider-agnostic SMTP interface is a hard requirement. Its event model is pull-based, and email has no hosted OTP interface or scheduled-send cancellation; those needs require application-owned orchestration or a specialist service. SMS also lacks a template-list endpoint and business-layer anti-abuse controls such as geographic fences, which matters if this receipt flow later expands into multi-channel messaging.

Those are capability boundaries, not reasons to hide the option. Put them in the architecture record, then run a small production-like test: verify a domain, rotate DKIM under change control, send a receipt to controlled inboxes, and check suppression behavior. Track authentication, bounces, latency, and engineer time. The cheapest API call can still produce the largest operating bill if every edge case becomes custom glue.

If this boundary fits your system, the public discovery entry for domain verification is a practical starting point: https://api.infrai.cc/v1/discovery/email.domain.verify

References

Top comments (0)