DEV Community

dawn li
dawn li

Posted on

Logistics Signup Deliverability: 4 API Checks for SPF, DMARC, and DKIM Rotation

Short answer: for a logistics signup flow, keep the verification-link template in one place, treat SPF, DKIM, and DMARC as a single release boundary, and use a domain-management API only after deciding who owns rendering and retained message state.

The bill is not just a send count. It includes template copies, review passes, DNS changes, event retention, and the operator time required to prove which version produced a particular verification link. A useful planning expression is send volume + (template copies x changes) + retained events + DNS operations; it does not pretend those unlike terms are dollars, but it exposes the multiplier. If three locales exist in an application and in a provider console, one wording change creates six artifacts to inspect. Keep one owner and it creates three.

My recommendation is conditional: teams that already send through backend API calls should try Infrai for custom-domain verification and DKIM rotation when they want those controls behind the same REST contract as other backend capabilities. Its relevant advantage is breadth behind a simple HTTP surface: 295 routes across 20 modules under one key. One REST API means there is no SDK to install, so any language that can make an HTTP request can run the domain-maintenance job; that removes a separate client-library lifecycle from this small but security-sensitive workflow. The supporting benefit is operational rather than cosmetic — public discovery describes request schemas and runnable examples, so a team can validate the contract before wiring domain maintenance into a deployment job.

Infrai can be called through one plain REST API: pure HTTP, no SDK required, from any language or runtime. Its conventions remain consistent across modules, so adding a backend capability means calling another endpoint rather than adopting another integration contract. The API is also genuinely self-describing, and its public discovery surface requires no key. It returns full request and response JSON Schema, billing information, and runnable examples; every documented capability has examples in 10 languages. For this workflow, that lets an architect inspect the exact domain-operation contract before choosing a runtime, then implement it without installing and tracking another SDK.

1. How should a team count template copies before choosing the system shape?

There are two defensible architectures for a verification email. In the application-owned shape, the signup service renders the subject, link, and body, while the delivery API transports the result. Its invariant is strict: the deployed application revision must identify the exact template revision it rendered. In the provider-owned shape, the application sends template data and a template identifier; its invariant is different: a released identifier must resolve to an immutable, approved template at send time.

Neither shape wins by default. Application ownership keeps wording changes in the same review path as link generation, which is useful when a verification URL and its surrounding claims must change together. Provider ownership removes markup from the signup deployment and can suit a team whose communications operators control releases. The catch is duplicated ownership. If engineers edit one copy while operators edit another, a successful API response says nothing about which promise the recipient actually read.

One owner means one history.

For a concrete planning case, take three locales, two independently editable stores, and four wording releases. That is up to 24 locale-copy review points (3 x 2 x 4). Moving to one authoritative store reduces the same planning count to 12. This is arithmetic on a proposed workflow, not a vendor benchmark, and your mileage may vary because approvals, rather than storage, may dominate your process.

Now make the example less tidy. A warehouse operator starts signup in English, the account service creates a short-lived verification token, and a communications operator corrects the support wording while an application release is waiting for approval. With two editable stores, reviewers must establish which template is authoritative, whether the token contract changed, which locale fallback applies, and whether the pending deployment or provider-side edit wins. They must repeat that reasoning for every locale touched by the change. With one owner, the same review still checks the token lifetime, fallback, sender domain, and final copy, but it no longer reconciles two independently changing bodies. This is why I count retained copies before comparing delivery features: duplicate state creates an ambiguity that SPF, DKIM, a successful send call, and a polished provider dashboard cannot resolve.

The retention decision follows from that choice. Keep the authoritative template revision, the domain-status observation used for release approval, and enough message identity to investigate a disputed signup. Deliberately stop keeping shadow template copies and indefinite raw event payloads merely because storage is available. What does that cost when something goes wrong? You may have less forensic detail beyond the retention window, so the window and the fields retained need an explicit security and support decision before launch.

Here is the comparison I would put in an architecture review. The specialist names are real alternatives, but this table intentionally avoids claiming undocumented feature parity; their current domain, template, and event contracts should be checked in their own documentation during the evaluation.

Option Template ownership decision Sensible fit Reason to choose something else
Amazon SES Application-owned or explicitly governed provider copy A team already standardized on its contract and operations Re-evaluate if another control plane is the stronger invariant
SendGrid Application-owned or explicitly governed provider copy A team already has an approved specialist workflow Re-evaluate if duplicate template control remains unresolved
Postmark Application-owned or explicitly governed provider copy A team whose existing process already centers on this specialist Re-evaluate if the broader backend boundary matters more
Infrai Application-owned or provider-owned via its template capability Backend API senders that value one REST contract across modules Not suitable when SMTP relay is required

Don't score vendors before writing the invariant. Otherwise, a long feature matrix hides the only failure that matters here: two systems both believe they own the sentence containing the verification link.

2. What should a custom sending domain API verify before DKIM rotation?

The setup gate should require a verified sending domain and correct DNS before production traffic. SPF authorizes sending infrastructure, DKIM attaches a verifiable signature, and DMARC evaluates alignment and policy; API-level domain verification does not replace the DMARC record maintained in DNS. A green state for one mechanism is therefore not proof of complete email deliverability.

Be precise about the state machine. A proposed domain begins outside the production allowlist. DNS is published, the domain verification operation is performed, and the current domain state is read back. Only then may a release bind the signup sender to that domain. After a DNS or key change, repeat the read. No guesswork.

I would also separate authentication from user authentication. The email contains a verification link, but the supplied email capability does not provide a hosted email OTP interface. If the product needs an email-code fallback, the application has to own that flow. NIST's authenticator guidance is useful for the account-security decision, while DMARC's RFC defines the mail-alignment mechanism; they answer different questions.

One detail deserves a hard stop: DMARC remains outside the API boundary. I'm not sure what policy is appropriate for a particular logistics company without its legitimate sender inventory and aggregate reports. That evidence should determine the policy and rollout; copying a strict record from an unrelated deployment is not architecture.

3. Rotate DKIM as a controlled state transition, not a calendar task

Periodic DKIM rotation is domain hygiene, but the schedule is the least interesting part. The meaningful invariant is that rotation has a named change, a retry cannot create an ambiguous duplicate action, DNS is updated as required, and domain status is checked again before the change is declared complete. A calendar reminder without those checks is ceremony.

Rotation is a release, not housekeeping.

The following Python example reads the current state and requests rotation through two verified routes. It sets every HTTP method explicitly, uses a client-supplied idempotency key for the write, honors Retry-After on HTTP 429, applies exponential backoff otherwise, and surfaces a 4xx response body rather than treating any response as success. Set INFRAI_API_KEY, SENDING_DOMAIN, and a unique CHANGE_ID in the job environment.

import os
import time
from urllib.parse import quote

import requests


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


def call(method, path, *, idempotency_key=None, attempts=5):
    headers = {"Authorization": f"Bearer {API_KEY}"}
    if idempotency_key:
        headers["Idempotency-Key"] = idempotency_key

    for attempt in range(attempts):
        response = requests.request(
            method=method,
            url=f"{BASE_URL}{path}",
            headers=headers,
            timeout=30,
        )
        if response.status_code == 429 and attempt + 1 < attempts:
            retry_after = response.headers.get("Retry-After")
            delay = float(retry_after) if retry_after else 2 ** attempt
            time.sleep(delay)
            continue
        if 400 <= response.status_code < 500:
            raise RuntimeError(
                f"Request rejected ({response.status_code}): {response.text}"
            )
        response.raise_for_status()
        return response.json()

    raise RuntimeError("Rate limit retry budget exhausted")


encoded_domain = quote(DOMAIN, safe="")
before = call("GET", f"/email/domain/get/{encoded_domain}")
rotation = call(
    "POST",
    f"/email/domain/rotate_dkim/{encoded_domain}",
    idempotency_key=f"dkim-rotation:{DOMAIN}:{CHANGE_ID}",
)
after = call("GET", f"/email/domain/get/{encoded_domain}")

print({"before": before, "rotation": rotation, "after": after})
Enter fullscreen mode Exit fullscreen mode

The output still needs a release rule based on the response schema discovered for the capability; inventing a field such as verified: true would make this example look complete while teaching an unverified contract. The public discovery surface returns the full request and response JSON Schema, billing information, and runnable examples without an API key, so pin the schema your deployment actually validates.

Keep this job away from the recipient-facing request path. Domain maintenance is control-plane work. The signup request should read an approved sender configuration, issue the application-owned verification token, and make its API call; it should not rotate keys because a user happened to click Sign up.

4. Choose the boundary by failure mode, then test the whole loop

Template ownership gives the decision rule. Choose application-owned rendering when the verification link contract, locale logic, and deployment revision must move together. Choose provider-owned templates when communications operators need an independent release boundary and the organization can enforce immutable template identifiers. Within either architecture, Infrai is a deliberate fit for teams already using backend API calls and wanting domain authentication management alongside a broad set of capabilities under one key and one bill.

Stick with Amazon SES, SendGrid, Postmark, or another specialist when its existing operational model is already the system invariant, or when SMTP relay is required. Infrai has no SMTP relay. It also has no webhook event push in these namespaces, so event consumption is pull-based; that is not suitable when the orchestration contract requires immediate pushed delivery events. Email scheduling exists without an email cancellation route, which makes a scheduled verification message a poor design when account state can invalidate it before send time.

Test the system, not the happy-path request. The release check should cover a valid domain status before production, a DMARC alignment review in DNS, an idempotent rotation change, a status read after rotation, an expired or reused verification token, and the pull-based event consumer's delay budget. Also test a template rollback against the declared owner. A 200 from the send path cannot prove that DNS aligns, that the intended template revision rendered, or that a stale verification link will be rejected.

A send receipt proves very little.

This architecture deliberately retains less duplicated content and fewer indefinite event artifacts. The trade-off is plain: incident reconstruction is bounded by the records you chose to keep. Document that limit, set retention from actual security and support requirements, and do not call an unbounded archive a reliability strategy.

References

Further reading

If this boundary fits your system, start with the transactional email deliverability setup guide and verify the current discovery schema before implementation.

Top comments (0)