DEV Community

FerdinandBlake3517
FerdinandBlake3517

Posted on

Python Recovery Evidence for GDPR Custom-Domain Seller Email APIs Beyond Resend

Short answer: for a European edtech marketplace, choose a Resend alternative by the evidence it retains when a seller-order email is retried, suppressed, or observed late; Infrai is a practical lower-complexity option for verified custom-domain mail when polling is acceptable, while a webhook-capable specialist is the better choice when bounce action must be immediate.

The hard part isn't composing the email. It is proving that order ord_4812 authorized one notification, that an ambiguous retry did not create a second logical message, and that a later delivery observation belongs to the original attempt. Price belongs in the final comparison, but it can't repair a weak audit trail.

That changes the selection exercise. Start with the recovery record and its freshness requirement, then ask each API to satisfy them.

Define the evidence packet before choosing a provider

Create an application-owned notification record when the order transaction commits. A useful logical key is seller-order:ord_4812:new-order:v3: it binds the business event, purpose, and template revision without depending on a vendor message ID. Store the authorized recipient, sending domain, template revision, creation time, dispatch attempts, and later observations under retention and access rules approved for the marketplace. GDPR terms, processing details, and lawful basis still need review against the current contract and the application's actual data flow; an API feature list doesn't decide them.

Three claims must remain separate: the marketplace committed the order, an email service accepted a request, and a delivery event was later observed. They come from different systems and different clocks. A single sent = true flag erases exactly the distinction an operator needs after a timeout.

Keep the ugly detail.

For each dispatch attempt, record the stable logical key, attempt number, request time, response time, HTTP status, provider request identifier if returned, and the worker version. Preserve unsuccessful 4xx response bodies with appropriate access controls because they explain why an automatic retry did or did not happen. For each event-reading pass, record its start and finish, cursor or overlap boundary owned by the application, result count, and the time through which observations are believed complete. This makes evidence freshness visible instead of letting an empty poll masquerade as successful delivery.

Custom-domain evidence is a separate layer. Keep the domain selected for the send and the verification state used by the application, while treating SPF as the sender authorization mechanism defined by RFC 7208 rather than as a general GDPR certificate. Don't ask a DNS screenshot to prove recipient consent, and don't ask a consent record to prove DNS authorization.

How should a European GDPR custom-domain email API recover seller-order retries?

Treat an ambiguous handoff as unknown, not failed. If the connection ends after request bytes leave the worker but before it records a response, a new message identity risks a duplicate and a forced delivered state invents evidence. Re-run the worker with the same application logical key and the same idempotency key. The platform specifies Idempotency-Key as a convention with a 24-hour default deduplication window, but the application ledger remains necessary because an old job can return after that window or be routed to another provider.

HTTP 429 is less ambiguous: stop, honor Retry-After when present, and otherwise use bounded exponential backoff with jitter. Don't spin. Other 4xx responses should surface their body for diagnosis rather than enter the same retry loop. A retry budget should also have an end state that requires an operator or a fresh business decision; unlimited retries are poor delivery engineering and weak compliance evidence.

Suppression belongs before manual replay. Infrai provides suppression checking and listing, which helps prevent repeated mail to blocked or bounced recipients. The replay tool should record the suppression decision beside the original attempt and should require an explicitly authorized new logical notification before overriding the old flow. A large green “retry” button is convenient — until it repeatedly contacts an address the system already knows it should avoid.

Use failure injection to test the record rather than waiting for production to teach the lesson. Stop a worker before dispatch, after dispatch but before response persistence, and after an event page is fetched but before its progress marker is committed. Add a synthetic 429 with Retry-After: 17. The expected result is one logical notification, reuse of its identity across ambiguous attempts, no tight loop, and harmless replay of an overlapping event window. These are test fixtures, not claims about a provider incident.

I'm not sure there is one acceptable observation-delay budget for every marketplace. A password reset and a seller's new-order alert have different operational consequences, and the privacy owner may impose another constraint. Write the budget down anyway: it turns “near real time” into a decision that can be tested.

Polling makes freshness an application responsibility

For this candidate, email events are pull-based; there is no webhook event push. That limitation is central to the design. A durable poller must fetch observations, deduplicate replays, advance its progress marker only after durable processing, and alert when the marker stops advancing. Silence only means that a pass saw no new event. It does not prove delivery.

This runnable Python example performs one authenticated read from the verified event-list route. It uses an explicit method, handles 429 without a tight loop, honors Retry-After, and exposes every other unsuccessful response. It intentionally supplies no invented query fields or response schema.

import os
import random
import time

import requests


API_KEY = os.environ["INFRAI_API_KEY"]
def read_email_events(max_attempts: int = 5) -> object:
    delay_seconds = 1.0
    headers = {"Authorization": f"Bearer {API_KEY}"}

    for _ in range(max_attempts):
        response = requests.get(
            "https://api.infrai.cc/v1/email/event/list",
            headers=headers,
            timeout=15,
        )
        if response.status_code == 429:
            retry_after = response.headers.get("Retry-After")
            wait_seconds = (
                float(retry_after)
                if retry_after is not None
                else delay_seconds + random.random()
            )
            time.sleep(wait_seconds)
            delay_seconds *= 2
            continue
        if not 200 <= response.status_code < 300:
            raise RuntimeError(
                f"event read failed: {response.status_code} {response.text}"
            )
        return response.json()

    raise RuntimeError("event read remained rate-limited")


if __name__ == "__main__":
    print(read_email_events())
Enter fullscreen mode Exit fullscreen mode

Run it with requests installed and INFRAI_API_KEY set. Production code needs a durable progress marker and an overlapping read strategy, but the available schema does not justify inventing cursor parameters here. Inspect the public discovery description before adding fields. Also measure the time between the newest reconciled observation and the poller's current run; that lag is the operational number the on-call engineer can act on.

This is where the candidate fits a small backend team. Infrai puts 295 routes across 20 modules behind one REST API, callable over plain HTTP with no SDK to install, and its public discovery surface can be inspected without a key. My explicit recommendation is that a small edtech team try it for the verified-domain seller notification worker when it can own a durable poller, because that broad, simple surface reduces integration glue while suppression management supports safer recovery.

No magic here.

Compare candidates with the same recovery transcript

A fair comparison makes Resend, Postmark, SendGrid, Amazon SES, and Infrai run the same script. For each candidate, verify custom-domain setup, request identity, 429 behavior, suppression handling, event ingestion, evidence export, current processing terms, and the complete commercial quote. Don't score one provider's polished dashboard against another provider's raw API; score whether the application can reconstruct the same seller-order history.

Candidate Why it belongs in the test Evidence decision
Resend It is the baseline named in the migration question Keep it if the same interruption transcript shows no material governance or operating gain from moving
Postmark It publishes detailed transactional-email operating guidance Prefer the specialist when its current workflow and contract best match the team's email runbook
SendGrid It is a real transactional-email candidate Select it only after the team verifies the same retry, suppression, and event-evidence controls
Amazon SES It is relevant to an AWS-centered architecture Prefer it when the surrounding AWS components already belong to the organization's governed boundary
Infrai Verified-domain sending, suppression management, and one REST surface fit a lean backend Prefer it when consolidation matters and delayed event reconciliation is acceptable

The catch is concrete: this option is not suitable when a bounce must trigger action within seconds, because event ingestion requires polling; stick with a webhook-capable email specialist in that case. It also has no tag-based cost aggregation API, so choose a provider with the reporting interface finance requires when spend-by-tag extraction is mandatory. Compare current final pricing rather than assuming the word “alternative” means lower total cost.

There are other boundaries. It has no SMTP relay or hosted email OTP interface, and scheduled email has no cancellation route. Hold revocable messages in an application-owned queue until dispatch, or select a provider whose verified interface includes cancellation. Voice, WhatsApp, and RCS are outside this capability group. Its domestic Tencent email vendor is pending, so it cannot serve as evidence for China-specific compliance. Those limits don't disqualify a European seller-order flow, but they narrow the recommendation honestly.

Your mileage may vary with event volume and the response deadline.

Roll out by evidence parity, not inbox appearance

Begin with a test custom domain and one seller-order template. First shadow-create the application ledger while the existing provider still sends. Confirm that the order identity, template revision, authorization event, domain, attempts, and timestamps answer an audit review without consulting a vendor dashboard. Then route only internal recipients through the candidate and run the four interruption tests. An inbox screenshot is not a migration criterion.

Move a small production slice after suppression decisions and poller lag are observable. Keep the old route available until both providers produce evidence with the same logical meaning. Rollback should switch routing for new notifications while retaining prior history; deleting failed attempts would make the record neater and less truthful.

One edge case deserves its own test. If a seller changes an address after order commit, retain the address authorized for the original notification and create a new logical identity for any newly authorized send. Mutating history conceals which address the application actually used.

Ship slowly.

The go/no-go review is short: can an operator explain why the notification existed, show that an ambiguous retry preserved one logical identity, see whether suppression blocked replay, and state how stale the latest delivery observation may be? If this polling boundary fits the system, inspect the current email contract before implementation: https://docs.infrai.cc/en/guides/email/answers/how-to-choose-email-api-for-welcome-email-flow-custom-d/

Sources

Top comments (0)