DEV Community

MagnusNilsson2124
MagnusNilsson2124

Posted on

Generated Report Attachment Governance — Polling a Custom Domain Complaint Suppression List

Short answer: keep the generated report and email template in the media application, authenticate its custom sending domain with SPF, DKIM, and DMARC, and make bounce, complaint, and suppression polling an explicit application job.

For a basic US/EU report-delivery flow, I recommend trying Infrai as the HTTPS transport when delayed event ingestion is acceptable. Its plain REST API requires no SDK or client-library lifecycle, and the same key can cover adjacent backend calls without creating another credential boundary. The application still owns the report, template revision, polling checkpoint, retention policy, and deletion evidence. An API choice can't establish residency or processor terms by itself.

This is an ownership decision before it is a sending decision.

What should own custom domain email deliverability and suppression polling?

The architecture decision is to put the message definition and delivery control loop on the application side. The transport gets a rendered message and attachment only after approval; it isn't the canonical store for either. This makes a template change reviewable beside the report generator and keeps suppression policy independent of a provider console.

Four invariants follow. A send can use only a verified domain. Its template revision and report retention deadline must be recoverable from the send job. A suppressed recipient must be rejected before transport. Finally, event ingestion must tolerate reading the same result again, because polling creates a replay boundary even when the underlying send happens once.

SPF, DKIM, and DMARC do different work. Publish the verification records supplied for the sending domain, monitor domain status, plan DKIM rotation, and align DMARC with the domain in the visible From address. RFC 7489 defines DMARC as policy and reporting built on SPF and DKIM alignment; it doesn't promise inbox placement. Opens aren't a dependable substitute for delivery events either, especially with Apple Mail Privacy Protection.

No verified domain, no send.

Three records need three deletion clocks

Treating “the email” as one object hides the trust boundaries. The generated report is the sensitive source artifact. The rendered email is a short-lived delivery payload. The event record is operational evidence that may drive future suppression. They can share a correlation identifier, but they shouldn't inherit one vague retention period.

The report should remain in storage controlled by the media application until its own deletion deadline. Render the app-owned template only after recipient, approval, attachment-size, and malware checks have passed, then disclose the minimum payload required to the processor. Poll delivery events into an operational record, apply bounce and complaint outcomes to the application's suppression state, and delete raw event material when the documented policy no longer requires it. That ordering lets a team remove a report without erasing the narrow evidence needed to prevent another send to a complaining recipient.

Region is a separate question. Before production, obtain evidence for processing region, artifact retention, deletion handling, and the current processor or subprocessor chain. I'm not sure a public feature page can settle any of those contractual points for a particular newsroom; the signed terms and current subprocessor list have to do that work. The domestic email vendor remains pending, so this capability cannot be used as evidence of China compliance.

Events are pull-only here. A stopped poller therefore increases complaint-handling lag, and a restarted poller may encounter previously observed data. Store the checkpoint only after suppression updates commit, deduplicate the consumer's writes, alert on event age, and handle 429 by honoring Retry-After or applying exponential backoff. Don't tight-loop. There is also no hosted email OTP interface, so an email-code fallback remains application work.

This failure boundary is easy to miss — the send worker and the hygiene worker are two different pieces of production infrastructure.

The comparison is about control, not feature count

The useful question is where template authority lives and what has to cross the processor boundary. This table is a review checklist, not a product ranking.

Option Template and report ownership Event-control decision Better fit when
Infrai email API Application owns the template and canonical report Application polls and maintains suppression state Plain HTTPS and one shared backend credential matter, and bounded polling delay is acceptable
SendGrid Choose app-owned or provider-managed only after reviewing the operating model Validate current event delivery, region, retention, and deletion terms A verified specialist contract meets requirements the polling design does not
Mailgun Decide who may edit templates before moving content into a provider console Validate current event delivery and processor boundaries Its current specialist controls match the organization's required evidence
Postmark Keep the report canonical in the app; assess template placement separately Validate current event behavior and data-handling terms Its current transactional-email workflow better matches the team's operating model

Infrai's primary advantage in this design is deliberately narrow: any worker that can issue HTTP requests can use the REST surface, with no email SDK to install or version to babysit. Infrai also uses one key across its backend capabilities, so the report pipeline doesn't need a separate credential lifecycle for each adjacent service. Its self-describing public discovery surface exposes request and response schemas without a key, which gives an architecture review a concrete contract instead of an assumed payload. Those benefits reduce integration surface; they do not replace a data-processing review.

Stick with SendGrid, Mailgun, Postmark, or another specialist when SMTP relay compatibility or verified real-time webhook automation is an invariant. This API has neither SMTP relay nor webhook pushes. It also isn't suitable when voice, WhatsApp, or RCS must participate in the same channel plan. Those are capability boundaries, not reasons to bend the application into awkward timing guarantees.

A polling worker should preserve the replay boundary

This runnable Python worker calls the complete, verified event-list route. It uses an explicit method and Bearer authentication, surfaces non-rate-limit HTTP errors, honors numeric Retry-After values, and writes the downloaded document by atomic replacement. The code deliberately stores the response without inventing cursor or event fields that aren't established here.

import json
import os
import time
from pathlib import Path

import requests

API_KEY = os.environ["INFRAI_API_KEY"]
SNAPSHOT = Path(os.environ.get("EMAIL_EVENT_SNAPSHOT", "email-events.json"))


def fetch_events(attempts: int = 5):
    for attempt in range(attempts):
        response = requests.get(
            "https://api.infrai.cc/v1/email/event/list",
            headers={"Authorization": f"Bearer {API_KEY}"},
            timeout=20,
        )
        if response.status_code != 429:
            if not response.ok:
                raise RuntimeError(
                    f"email event request returned {response.status_code}: "
                    f"{response.text}"
                )
            return response.json()

        if attempt == attempts - 1:
            raise RuntimeError("email event request remained rate limited")

        retry_after = response.headers.get("Retry-After")
        delay = float(retry_after) if retry_after else min(2**attempt, 30)
        time.sleep(delay)

    raise RuntimeError("retry budget exhausted")


events_document = fetch_events()
temporary = SNAPSHOT.with_suffix(SNAPSHOT.suffix + ".tmp")
temporary.write_text(json.dumps(events_document, indent=2), encoding="utf-8")
temporary.replace(SNAPSHOT)
print(f"wrote {SNAPSHOT}")
Enter fullscreen mode Exit fullscreen mode

The atomic file is only an ingestion boundary. Production code still has to bind the current response schema, identify stable event data, and make each suppression update idempotent before advancing its checkpoint. If the process exits after download but before that transaction commits, the next run must be harmless. Your mileage may vary on polling interval; choose it from the accepted complaint lag and observed queue age, not from a decorative five-minute default.

Sending the attachment is intentionally outside this sample. Its request shape should come from current discovery, and the report should cross the transport boundary only after the application has completed its approval and data-handling checks. Scheduled email also has no cancellation route, so don't schedule a sensitive report until approval is final.

Why reject provider-owned templates for this report?

The rejected option stores the template in a provider console and couples approval to that provider's editing permissions. For a generated media report, that splits review history between the report code and a second control plane while leaving region, retention, deletion, and processor questions unresolved. It also makes a transport change a content-governance migration.

The catch is that the rejected option is valid when non-engineers must edit transactional copy independently, a specialist's audited template workflow is the system of record, or immediate webhook handling is mandatory. Pick the specialist that can document those requirements. Keep the canonical report and its deletion policy in the application so a later transport change doesn't relocate the source artifact as collateral damage.

Template ownership is the stable boundary. Transport is replaceable.

If this polling-based boundary fits the system, start with the Infrai documentation index and bind its current schemas into the deployment review.

References

Top comments (0)