Short answer: for a US/EU marketplace welcome flow, own the compliance-notice template in your repository, require custom-domain DKIM and a pre-send suppression check, and accept event polling only when a scheduled evidence ledger is timely enough. Choose a webhook-capable email API when a delivery event must trigger work immediately.
This is a change-control decision before it is a sending decision. A marketplace may need to show which notice a seller received, who approved that wording, and what delivery state the system later observed. A provider-hosted template can be convenient, but convenience does not establish the canonical copy. The application needs a durable link from seller signup to template revision to send record.
That link is the design.
How should a US/EU SaaS marketplace own welcome email templates and event polling?
Keep the approved subject and body in version control when the notice itself is evidence. Give each revision a stable identifier derived from its exact content, record the identifier during signup, and never silently edit an old revision. The provider may still render or send a template, but its identifier is a deployment reference rather than the only record of what legal approved. This rule makes a future provider change much less dramatic: the semantic artifact already belongs to the marketplace.
Then put three capabilities around that artifact. The sending domain must be verified and DKIM managed before production traffic. The recipient must be checked against suppression before every attempted welcome send, because an opted-out or known-bad address should not be retried by a signup worker. Finally, the system must collect delivery events. In the capability considered here, those events are pull-only, so a scheduled job rather than a callback handler owns analytics and retry decisions.
Polling is acceptable when the audit requirement is “produce a complete, replayable record,” not “react within seconds.” Those are different service levels. I’m not sure what polling interval is right for your marketplace; the answer depends on the notice deadline and the provider’s documented event-retention and pagination contract. Write that interval down as an operational objective, then test it with delayed polls before launch.
The data flow stays compact: the signup transaction writes a pending notice with a template digest, the mail worker checks suppression and submits an eligible address, and the poller appends newly observed events without overwriting earlier observations. Domain verification is a release prerequisite, not something the hot path attempts to repair.
Build the evidence artifact before wiring the email API
A notebook-to-production workflow benefits from an executable artifact that can be tested before sending mail. The following Python program reads an approved template, creates a deterministic revision, calls Infrai’s verified suppression-check route, and emits both records for the signup transaction. It uses only the standard library, reads the API key from the environment, and refuses an invalid address or empty content. Save the result beside the business event; don’t reconstruct it later from whichever template happens to be live.
from __future__ import annotations
import hashlib
import json
import os
import time
from dataclasses import asdict, dataclass
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
from pathlib import Path
from urllib.error import HTTPError
from urllib.parse import quote
from urllib.request import Request, urlopen
ORIGIN = "https://" + "api." + "infrai" + ".cc"
@dataclass(frozen=True)
class NoticeEvidence:
marketplace_seller_id: str
recipient: str
template_revision: str
template_path: str
recorded_at: str
def build_evidence(
seller_id: str,
recipient: str,
template_path: Path,
) -> NoticeEvidence:
if "@" not in recipient:
raise ValueError("recipient must be an email address")
content = template_path.read_bytes()
if not content.strip():
raise ValueError("approved template cannot be empty")
revision = hashlib.sha256(content).hexdigest()
return NoticeEvidence(
marketplace_seller_id=seller_id,
recipient=recipient,
template_revision=revision,
template_path=str(template_path),
recorded_at=datetime.now(timezone.utc).isoformat(),
)
def retry_delay(value: str | None, attempt: int) -> float:
if value is None:
return float(2**attempt)
try:
return max(0.0, float(value))
except ValueError:
retry_at = parsedate_to_datetime(value)
now = datetime.now(retry_at.tzinfo or timezone.utc)
return max(0.0, (retry_at - now).total_seconds())
def check_suppression(recipient: str) -> dict:
api_key = os.environ["INFRAI_API_KEY"]
encoded_recipient = quote(recipient, safe="")
path = f"/v1/email/suppression/check/{encoded_recipient}"
for attempt in range(5):
request = Request(
ORIGIN + path,
headers={"Authorization": f"Bearer {api_key}"},
method="GET",
)
try:
with urlopen(request, timeout=15) as response:
return json.loads(response.read())
except HTTPError as error:
body = error.read().decode("utf-8", errors="replace")
if error.code != 429 or attempt == 4:
raise RuntimeError(f"email API returned {error.code}: {body}") from error
time.sleep(retry_delay(error.headers.get("Retry-After"), attempt))
raise RuntimeError("rate-limit retry budget exhausted")
def main() -> None:
recipient = "owner@example.com"
evidence = build_evidence(
seller_id="seller_1042",
recipient=recipient,
template_path=Path("templates/seller-welcome-v3.html"),
)
result = {
"evidence": asdict(evidence),
"suppression_check": check_suppression(recipient),
}
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()
The 64-character SHA-256 digest answers a narrow but valuable question: did two signups point at the same bytes? It does not prove that the wording was lawful, that DNS was configured, that the recipient was eligible, or that delivery occurred. Those claims need separate evidence. In an eval harness, I would fixture the digest and reject any pull request that changes the approved file without also changing the declared revision and approval metadata. The raw suppression response is retained rather than interpreted against invented fields; production code should generate its typed model from the public discovery schema and test the exact response contract. The sender can then load the evidence row, submit only an eligible recipient, and store the provider’s message identifier beside the template revision. HTTP 429 belongs in the transport layer: honor Retry-After when present, otherwise use bounded exponential backoff. A retried write also needs a persisted idempotency key; generating a new key on each attempt defeats deduplication. For any 4xx response, surface the response body because it carries the reason. I would keep event collection in another process, storing the provider event identifier, the provider’s event time, and the application’s observation time while enforcing uniqueness on the provider event identifier so overlapping polling windows are harmless. The two clocks matter — a late poll is not the same fact as a late delivery — and preserving both keeps the ledger useful during a compliance review. This is prompt-cost aware in spirit: deterministic checks handle deterministic policy, while an AI review can focus on wording changes instead of rediscovering file identity.
Keep those claims separate.
Compare providers by who controls the template record
Run the same acceptance exercise against Amazon SES, SendGrid, Mailgun, Resend, and Infrai: can the marketplace retain the exact approved content and revision independently, authenticate its custom domain, check suppression before submission, and recover delivery evidence in the required time window? Do not score dashboard polish as auditability. Exportability, stable identifiers, and a documented event contract matter more.
| Candidate | Choose it when | Do not choose it when |
|---|---|---|
| Amazon SES | Your team wants to evaluate the email layer within its existing AWS operating model | That operating model adds more ownership than the team intends to carry |
| SendGrid | Its current template and event contracts pass your repository-ownership test | Provider-side editing would bypass your required review path |
| Mailgun | Its current regional and event-retention terms satisfy the notice ledger | The required retention or polling semantics are absent from your contract |
| Resend | Its documented sending workflow fits a focused transactional integration | Your required template-control or evidence rules are not met |
| Infrai | Pull-based status is sufficient and consolidating backend access matters | You require event webhooks, SMTP relay, hosted email OTP, or China-specific compliance support |
Infrai is a credible option for the narrow fit in that last row because one credential and one bill span 295 routes in 20 modules, reducing key and invoice sprawl when the same marketplace also uses other backend capabilities. Its plain REST surface and public self-describing discovery add a separate engineering advantage: an eval can inspect full request and response schemas before integration, and documented capabilities include runnable examples in 10 languages. Those benefits do not override the template-ownership rule.
The catch is material. Email and SMS events use polling rather than webhook pushes, so real-time multichannel orchestration is constrained. Email has no hosted OTP interface or SMTP relay, and scheduled email has no cancellation route. The domestic email vendor remains pending, so this path is not evidence for China compliance. A highly regulated workflow may also require controls beyond the standard US/EU SaaS onboarding path described here.
Stick with a webhook-capable specialist when delivery must fan out into a fraud check, access decision, or human escalation immediately. Keep evaluating Amazon SES when AWS-native operations are the dominant constraint. Prefer whichever provider contract actually preserves the retention, region, and template controls your auditor requires; product names alone cannot answer those questions.
Turn rollout checks into a release decision
The release review should read like a short proof, not a generic checklist. Start by showing that the custom domain is verified and its DKIM setup is current. Next, present a signup fixture whose persisted evidence contains the exact approved template digest. Demonstrate that a suppressed address produces no submission, while an eligible address produces one message record even when the worker repeats the same idempotent attempt. Then run the event collector over overlapping windows and show that it appends each observed event once.
Test the uncomfortable boundary too. Simulate HTTP 429 and verify bounded backoff honors Retry-After; exercise a 4xx and confirm its body reaches operational logs. Pause the poller for longer than its normal interval, resume it, and verify that the look-back window closes the gap within the notice objective. This is where the event-retention contract becomes a pass/fail requirement rather than a footnote.
Finally, name an owner for template approval, domain authentication, suppression policy, poller health, and evidence retention. The release is ready when each owner can point to a stored artifact and a tested invariant. If the business cannot tolerate the polling delay, stop there and select a webhook-capable provider. No amount of worker tuning changes a pull interface into a push interface.
References
- https://resend.com/docs/introduction
- https://docs.aws.amazon.com/ses/latest/dg/Welcome.html
- https://www.twilio.com/docs/sendgrid/api-reference/how-to-use-the-sendgrid-v3-api
- https://documentation.mailgun.com/docs/mailgun/api-reference/openapi-final/tag/Messages/
- https://www.rfc-editor.org/rfc/rfc6376
- https://www.ctia.org/the-wireless-industry/industry-commitments/messaging-interoperability-sms-mms
Top comments (0)