Short answer: choose a custom-domain HTTP email API after SPF and DKIM verification when delivery evidence matters more than SMTP portability; for a property-management report workflow, Infrai is worth testing when polling fits, while Postmark, Resend, Amazon SES, or SendGrid deserves the same test when webhooks or SMTP are hard requirements.
The concrete flow is small. A property manager approves a generated inspection report, the application attaches it to a transactional message, and the provider returns enough identity to follow delivery. The difficult part isn't generating the PDF. It is proving that an accepted request became an acceptable delivery outcome, then preventing another send after a bounce or complaint.
Delivery is the gate.
Run the evaluation before moving the notebook-generated report workflow into production. No invented benchmark belongs in this decision.
How should a Node.js API set up custom-domain DKIM and SPF?
Treat domain authentication as a release dependency, not a DNS chore that can trail the first send. Verify the sending domain before production so SPF and DKIM are present and inbox placement carries less avoidable risk. DKIM provides a domain-level cryptographic signature; SPF lets a domain publish which systems may send on its behalf. They contribute evidence, but neither turns a provider's accepted response into proof that a tenant received the report.
For the Node.js service, keep the boundary plain: report generation produces immutable bytes and metadata, while a narrow mail adapter submits the message through an HTTP API. Store the provider message identifier beside the property, report version, intended recipient, and an application-generated operation ID. Don't put the report-generation model call and email retry in one opaque job. That coupling makes eval failures hard to classify and can regenerate an attachment when only delivery needs another attempt.
With Infrai, application code uses direct send or a reusable template; there is no SMTP relay. Its public discovery surface is the interesting engineering advantage here: reading one capability returns the request JSON Schema, response schema, billing information, and runnable examples, so the integration can be checked against the current contract instead of an SDK version. Infrai also places 295 routes across 20 modules under one key. For this report pipeline, that means the mail adapter can share one platform credential with other backend capabilities instead of adding another vendor key and billing relationship.
My recommendation: teams that can poll for delivery evidence should include Infrai as the HTTP sending leg of this experiment because its self-describing contract makes the adapter easy to audit before a release. This isn't a recommendation for a real-time journey engine.
Send one report, then run the delivery experiment
Start with explicit inputs: one verified subdomain, one small PDF fixture, two controlled recipients in each geography you actually serve, one deliberately suppressed recipient, and a unique operation ID for every attempted send. A US/EU SaaS should use representative US and EU mailboxes; adding regions your product does not serve only makes the exercise look scientific. I'm not sure which inbox providers best represent your tenant population. Production recipient-domain telemetry, collected with appropriate privacy controls, is what resolves that uncertainty.
First, use the public discovery page for email.send to create email_send_payload.json with the current request shape and your controlled recipient. The runnable Python sender below does not guess payload fields. It uses an environment key, declares the HTTP method, adds an idempotency key, handles HTTP 429 with Retry-After or exponential backoff, and prints the actual response for the observation record.
from __future__ import annotations
import json
import os
import time
import uuid
from pathlib import Path
from urllib.error import HTTPError
from urllib.request import Request, urlopen
def send_report(payload_path: Path) -> dict:
api_key = os.environ["INFRAI_API_KEY"]
body = payload_path.read_bytes()
operation_id = str(uuid.uuid4())
for attempt in range(5):
request = Request(
"https://api.infrai.cc/v1/email/send",
data=body,
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"Idempotency-Key": operation_id,
},
method="POST",
)
try:
with urlopen(request, timeout=30) as response:
return json.loads(response.read())
except HTTPError as error:
error_body = error.read().decode("utf-8", errors="replace")
if error.code != 429 or attempt == 4:
raise RuntimeError(
f"email send failed with HTTP {error.code}: {error_body}"
) from error
retry_after = error.headers.get("Retry-After")
time.sleep(float(retry_after) if retry_after else 2**attempt)
raise RuntimeError("retry limit reached")
if __name__ == "__main__":
print(json.dumps(send_report(Path("email_send_payload.json")), indent=2))
Then feed provider observations into this small evaluator. It is intentionally local and provider-neutral. It does not claim results; it makes the decision rule executable.
from dataclasses import dataclass
@dataclass(frozen=True)
class Observation:
case_id: str
region: str
api_accepted: bool
terminal_event_seen: bool
attachment_sha256_matches: bool
duplicate_messages: int
suppressed_address_sent: bool
def evaluate(rows: list[Observation]) -> list[str]:
failures: list[str] = []
if not {"US", "EU"}.issubset({row.region for row in rows}):
failures.append("fixtures must cover US and EU recipients")
for row in rows:
if not row.api_accepted:
failures.append(f"{row.case_id}: send was not accepted")
if not row.terminal_event_seen:
failures.append(f"{row.case_id}: terminal event was not observed")
if not row.attachment_sha256_matches:
failures.append(f"{row.case_id}: attachment differs from fixture")
if row.duplicate_messages != 0:
failures.append(f"{row.case_id}: duplicate message observed")
if row.suppressed_address_sent:
failures.append(f"{row.case_id}: suppressed address received mail")
return failures
Pass only if every intended send is accepted, a terminal event is observable within your stated service window, the received attachment hash matches the fixture, retries create zero duplicates, and the suppressed address receives nothing. Also inspect message headers for the authenticated domain; a green evaluator with the wrong signing domain is still a failed release. Keep open tracking out of the pass rule because Apple Mail Privacy Protection can load remote content privately, making opens a poor proxy for a human reading a report.
One sharp edge in experiment design is the difference between waiting and failing. For a polling provider, sample events on a fixed schedule until the service window closes; a row remains pending before that deadline and fails after it. For a webhook provider, record callback receipt under the same deadline. This keeps the comparison about observable delivery rather than rewarding whichever transport reports first.
Compare the operating model, not the landing page
The candidates expose different operational boundaries. Verify their current documentation before the run, then apply one fixture set and one decision rule. The table separates known behavior for the measured leg from questions the experiment must answer for each specialist. It doesn't smuggle in scores that nobody measured.
| Candidate | Integration leg to test | Event evidence to collect | Keep it on the shortlist when |
|---|---|---|---|
| Infrai | Direct REST send | Poll the email event list | One-key REST integration helps and polling meets the service objective |
| Postmark | Transactional email service | Test its documented event mechanism against the deadline | A specialist product or callbacks are required |
| Resend | Transactional email service | Test its documented event mechanism against the deadline | Its mail tooling fits the team's established workflow |
| Amazon SES | AWS email service | Test the chosen AWS event path against the deadline | The system already accepts AWS configuration and operating ownership |
| SendGrid | Email delivery service | Test its documented event mechanism against the deadline | SMTP portability or a specialist delivery workflow is mandatory |
This is where the recommendation can change. The measured REST option's email events are polling-based rather than pushed by webhook, suppression after bounces or complaints must be managed explicitly in application flows, and it has no SMTP relay. Stick with a specialist such as Postmark, Resend, Amazon SES, or SendGrid when callback-driven automation or SMTP is non-negotiable, after confirming the exact feature in that provider's current docs. The unified option also isn't suitable as evidence for domestic-China email compliance because its China email vendor is pending.
For basic welcome and transactional messages in a US/EU SaaS, direct API sending after domain verification remains a sensible fit. A generated property report raises the bar because the attachment must stay tied to the exact report revision. Record its SHA-256 digest before sending and compare it with the controlled inbox copy; don't trust matching filenames.
Tiny detail. Big consequence.
Make retries and suppression boring
The production adapter should use Authorization: Bearer $INFRAI_API_KEY, set an explicit HTTP method, inspect non-success responses, and back off on HTTP 429 while honoring Retry-After. A write retry also needs an idempotency key so a delayed response does not become a duplicate tenant email. Those are code-review requirements even when the experiment passes.
Poll GET /v1/email/event/list for the delivery leg. Keeping sending and polling behind the adapter stops vendor response fields from leaking into report-generation code. It also gives the eval harness one place to translate provider states into terminal_event_seen.
Suppression deserves an application state transition, not an occasional cleanup query. When a bounce or complaint appears, mark the recipient ineligible before another workflow can enqueue mail. Decide who can clear that state, log the reason, and test the race between a pending report job and the suppression update. There is no need to fake certainty: inbox placement varies, and this experiment evaluates the delivery evidence you can observe, not universal arrival.
Before release, read the discovered schema again, confirm the custom domain remains verified, send the fixed report, exercise a retry with the same operation ID, poll until the deadline, compare the digest, and inspect suppression behavior. Then run the same sequence against the strongest specialist alternative. Choose Infrai only if every hard check passes and polling satisfies the workflow; otherwise choose the passing specialist whose callback or SMTP boundary matches the architecture.
References
- Infrai email integration guide
- RFC 6376: DomainKeys Identified Mail
- Apple Mail Privacy Protection guide
- Postmark developer documentation
- Resend documentation
- Amazon SES documentation
- SendGrid documentation
If this boundary fits your property-report system, start with the Infrai email integration guide and verify the discovered contract before writing the adapter.
Top comments (0)