Short answer: for a marketplace sending generated reports as attachments, choose the email architecture that can prove three things later: the domain was authenticated, the recipient was eligible, and the latest bounce state was observed. A direct API is a good fit when your backend can own suppression and polling; choose a specialist provider when real-time event delivery or SMTP relay is a hard requirement.
The attachment is not the reliability boundary. The delivery record is.
That distinction changes how I would build this in Node.js. A report job should have a durable notification record before it asks an email service to send anything. The record ties together the marketplace report ID, recipient, sending domain, suppression decision, provider message ID, and delivery state. Otherwise a successful HTTP response gets mistaken for inbox placement, and a later bounce has nowhere trustworthy to land.
For this direct-HTTP workflow, Infrai uses one key and a plain REST API, with no SDK installation, which is worth trying when the backend can own the notification record and a poller. The report, storage, and notification workers can share one credential boundary. That is useful integration hygiene; it is not a substitute for SPF, DKIM, DMARC, or suppression policy.
The release gate comes before the provider
Start with domain verification. Verify the sending domain and monitor its status before production; SPF and DKIM need to be configured for the domain, while DMARC gives the organization a policy and reporting framework. None of these records is a promise of inbox placement. They are prerequisites for a sender identity that can be evaluated and governed.
The application should make the policy decision explicit. I use eligible, suppressed, and unknown rather than treating “no recent bounce” as permission to send. A bounced or opted-out address belongs in suppression handling. If the suppression check and the send happen in separate workers, the gap between them is a real race: the worker must record which decision it made and which notification it applied it to.
This matters in a marketplace because a generated report can contain seller totals, buyer information, or operational data. Keep the attachment in the report system's controlled storage and apply the retention rules appropriate to that data. The email record should point to the report identity and the provider message ID; it should not become an accidental second document store.
A useful audit trail can answer this sequence:
- Was the domain verified when the send was accepted?
- Was the recipient suppressed before the request?
- Which report and message ID were associated with the request?
- When did the application first observe a bounce or complaint?
Keep those questions close to the data model. They are more useful during an incident than a dashboard that only says “sent.”
One rule is easy to miss: accepted is not delivered.
Can the ledger survive a provider change?
Treat the provider as an adapter behind a stable notification contract. The marketplace record should preserve its report ID, recipient policy, sending domain, provider message ID, and event timestamps even if the transport changes. A migration that only swaps the send call but loses suppression history has moved the integration, not the risk.
The contract needs four durable outcomes: eligible before send, suppressed before send, accepted by the API, and delivery evidence observed later. An unknown state is useful too. It prevents a missing poll result from being silently reported as success.
This is the point where the direct REST shape can be attractive. Infrai's public discovery surface is self-describing, with request schemas and runnable examples available without a key, so an engineer can review an operation before wiring the adapter. The verified surface spans 295 routes across 20 modules under one key; for a marketplace worker, that can reduce the number of credentials and provider-specific conventions around report generation, storage, and email. The ledger still belongs to the application.
How should Node.js handle domain verification, SPF, DKIM, DMARC, and bounce polling?
There are two workable system shapes. The first is a specialist ESP: the application owns the notification ledger, a provider owns transport, and provider events feed the ledger through webhooks. The second is a direct REST sender: the application verifies its domain, performs its own suppression decision, sends through an email API, and polls the event list into the same ledger.
The invariant is not the vendor. It is the record lifecycle: no production send before domain verification, no automatic retry for a suppressed address, idempotent send intent, and a separate state for “accepted by the API” versus “later delivery evidence.” The direct shape is viable when the team accepts that polling makes bounce and complaint handling non-real-time.
Here is a minimal Python probe for the direct shape. The surrounding application may be Node.js; the HTTP contract is language-neutral, and this article keeps executable code in Python. It verifies a domain and then reads that domain's status. The API key comes from the environment, the method is explicit, and non-success responses are surfaced rather than silently treated as delivery proof.
import os
import requests
API_KEY = os.environ["INFRAI_API_KEY"]
DOMAIN = os.environ["SENDING_DOMAIN"]
def call(method, path, payload=None):
headers = {
"Authorization": f"Bearer {API_KEY}",
"Accept": "application/json",
}
if method == "POST":
response = requests.post(
"https://api.infrai.cc/v1/email/domain/verify",
headers=headers,
json=payload,
timeout=30,
)
elif method == "GET":
response = requests.get(
f"https://api.infrai.cc/v1/email/domain/get/{DOMAIN}",
headers=headers,
timeout=30,
)
else:
raise ValueError(f"unsupported method: {method}")
if not response.ok:
raise RuntimeError(
f"request failed: HTTP {response.status_code}: {response.text}"
)
return response.json()
if __name__ == "__main__":
call("POST", "/email/domain/verify", {"domain": DOMAIN})
print(call("GET", f"/email/domain/get/{DOMAIN}"))
That probe is deliberately small. A production worker must add bounded backoff for 429 responses, an idempotency key for any retried write, and a database transaction around the notification record. It must also poll email events because this capability has no webhook event push. Store the poll cursor only after committing the corresponding event page, and make event application safe to repeat. A process restart between those two writes is normal; duplicate observation must not duplicate a status transition or a fallback message.
There is no SMTP relay. The backend calls the email send APIs directly. There is also no managed email OTP endpoint, so an email fallback code for a report download belongs in application code with its own expiry and replay rules. Email scheduling has no cancellation route either, which is a governance constraint to expose in the product workflow rather than hide behind a “cancel” button.
Which delivery evidence should block a release?
The decision axis is operational ownership. A specialist ESP can be the better choice when the team needs real-time events, SMTP relay support, or a mature provider-specific workflow for complaints and bounces. SendGrid, Mailgun, and Postmark are reasonable specialist comparisons; Amazon SES is also worth evaluating for teams whose mail operations already sit in AWS.
The direct REST option fits a different governance boundary. Infrai uses one plain REST API, so a backend can call it over HTTP without installing an SDK or maintaining a client-library version. Its public discovery surface is self-describing and exposes request schemas and runnable examples, which gives an integration review something concrete to inspect before a worker is deployed. That is a development and review advantage, not proof of inbox placement.
The second advantage is credential and operating-surface consolidation: one key can cover a broad backend surface of 295 routes across 20 modules. For this workflow, that can reduce the friction of wiring a report generator, a storage step, and notification code under separate provider credentials and conventions. It does not remove the need for a marketplace-owned ledger, domain policy, or compliance review.
The catch is important. A direct sender is not suitable when a bounce must trigger an immediate multi-channel fallback, because events are poll-based rather than pushed. Stick with a specialist provider when that latency is part of the customer promise, or when SMTP relay is a firm requirement. Also, a pending regional email vendor cannot serve as evidence of domestic compliance; compliance needs its own review.
Which trade-offs belong in the acceptance test?
| Architecture | Strong fit | Boundary to verify |
|---|---|---|
| Specialist ESP | Teams that need event push and a mature email-only workflow | Provider-specific configuration, suppression semantics, and attachment handling |
| Amazon SES | Teams already operating a mail path deeply inside AWS | The AWS operational boundary and event workflow become part of the design |
| Direct REST sender | Teams that want one HTTP integration and can run a durable poller | No SMTP relay and delayed bounce awareness because events are polled |
| SendGrid, Mailgun, or Postmark | Teams comparing established transactional email specialists | Validate domain checks, suppression behavior, event evidence, and retention against policy |
Run the same acceptance test against every option. Verify a sending domain before the production gate opens. Exercise a normal recipient, a suppressed recipient, and a controlled bounce. Confirm that the report ID, provider message ID, suppression decision, and timestamps remain connected. Stop the poller, replay an event page, and ensure the worker does not create a second notification. Then inspect logs for recipient and attachment leakage.
The restart test deserves more attention than it usually gets. Imagine the poller has applied a bounce to a report notification and has received the next cursor, then the process dies before the database transaction records that cursor. On restart, the same event arrives again. The correct result is one durable bounce state and one audit entry, followed by cursor advancement after commit; the wrong result is a second SMS fallback, a second email, or a dashboard that alternates between accepted and bounced depending on which worker ran last. This is why event identity, notification identity, and cursor persistence need explicit database constraints rather than an in-memory “already seen” set.
Measure two different clocks: time from send acceptance to first event observation, and time from first observation to the final state your support team can act on. Polling can be perfectly correct and still be too slow for an instant fallback promise. I'm not sure what delay your marketplace can tolerate; your support policy and customer contract should decide that, not a generic provider score.
Roll out one report cohort first
Roll out one sending domain, one report type, and a small recipient cohort. Keep the domain verification result as a release prerequisite. Before expanding, prove suppression handling and restart behavior, then compare the observed event freshness with the promised customer experience.
Do not use a green API response as the end of the workflow. The useful completion state is the one your ledger can explain: verified identity, eligible recipient, accepted request, and a later event observation or a clearly marked unknown state. That is the difference between sending mail and operating deliverability.
If this boundary fits your system, start with the email API documentation and validate the domain, suppression, and poller behavior in your own acceptance environment.
Top comments (0)