Short answer: The bill for an auditable healthtech compliance-notice mailer is shaped by sends, event polling, and retained evidence; without a published workload or price schedule, no honest dollar estimate follows. In a transactional app, poll message outcomes, suppress addresses after bounce or complaint-like results, and check suppression before another send. Keep a durable record of the notice version, recipient, send attempt, observed outcome, and observation time. A send request alone does not prove delivery.
For a concrete sizing exercise, 10,000 notices with one initial send each imply 10,000 send attempts before retries. Polling every five minutes means 288 polls a day, even if nothing changes. Those are workload assumptions, not vendor prices or measurements. The term to investigate first is whichever dominates your measured bill: outbound sends if each recipient gets one notice, or repeated event scans if the worker retrieves the same history on every poll. Measure both before choosing a shorter interval. For example, a job that downloads a full day's events 288 times should be evaluated differently from one that reads only new events; do not assume an incremental filter exists until its schema says so. The compliance record needs one durable observation per actual event, not another copy each time the worker sees it.
Count the reads.
What evidence does a compliance notice actually need?
Record the immutable notice version and the decision to send separately from the provider's event. An accepted send, a delivered event, and a complaint are different observations. None establishes that a person read the message. Store event timestamps and the time your worker observed them; a pull-based feed can report an outcome after the notice workflow has moved on.
Use a stable internal notice ID to deduplicate your own processing. Associate that ID with the recipient and the provider's message identifier when the provider returns one. Keep the payload narrowly scoped: a notice version or content digest is often a better audit artifact than retaining the whole email body. Do not silently turn a missing event into a delivered status.
How should an email worker poll bounce and complaint events before suppression?
Run the event poller in a backend job or cron worker. Classify delivered, bounced, and complaint-like outcomes using the provider's documented event schema; persist the source event identity where one exists, and make the classification step repeatable. Add bad addresses to the suppression list, then check suppression immediately before each send. A poller that fails midway should be able to replay its window without generating another send or erasing a prior adverse outcome.
The following runnable Python example reads the real suppression capability contract and exercises the local decision layer without guessing provider response fields. Set INFRAI_API_BASE to the provider's v1 API base and INFRAI_API_KEY to your credential. Wire the actual send and event-list request schemas from discovery into a backend worker before using it with live messages. The local event names below aren't provider response fields.
from datetime import datetime, timezone
import json
import os
import time
import urllib.error
import urllib.request
url = os.environ["INFRAI_API_BASE"].rstrip("/") + "/discovery/email.suppression.add"
for attempt in range(4):
request = urllib.request.Request(
url,
headers={"Authorization": "Bearer " + os.environ["INFRAI_API_KEY"]},
method="GET",
)
try:
with urllib.request.urlopen(request, timeout=15) as response:
capability = json.load(response)
break
except urllib.error.HTTPError as error:
if error.code != 429 or attempt == 3:
raise RuntimeError(f"HTTP {error.code}: {error.read().decode()}") from error
retry_after = error.headers.get("Retry-After")
time.sleep(float(retry_after) if retry_after and retry_after.isdigit() else 2 ** attempt)
print("Declared suppression path:", capability["path"])
events = [
{"notice_id": "policy-2026-07", "email": "a@example.test", "outcome": "delivered"},
{"notice_id": "policy-2026-07", "email": "b@example.test", "outcome": "bounced"},
{"notice_id": "policy-2026-07", "email": "c@example.test", "outcome": "complaint"},
]
suppressed = set()
audit = {}
def observe(event, observed_at):
identity = (event["notice_id"], event["email"], event["outcome"])
if identity in audit:
return
audit[identity] = {"outcome": event["outcome"], "observed_at": observed_at}
if event["outcome"] in {"bounced", "complaint"}:
suppressed.add(event["email"])
now = datetime.now(timezone.utc).isoformat()
for event in events:
observe(event, now)
observe(event, now)
for email in ("a@example.test", "b@example.test", "c@example.test"):
print(email, "hold" if email in suppressed else "eligible")
print("unique observations:", len(audit))
This demo deduplicates identical local observations, not distinct upstream events that happen to share an outcome. Production code needs the provider's actual event identity or a documented cursor, plus a transactional checkpoint; otherwise two bounces at different times can collapse into one audit row. Also distinguish a temporary delivery problem from an address that must remain suppressed. That policy needs an explicit review path rather than an automatic retry loop. Suppression writes need a stable idempotency key and a request assembled from the returned schema, and send retries need their own deduplication policy. Never infer a delivery event from a successful lookup.
Check the adverse outcomes first.
Which provider fits the evidence requirement?
Amazon SES is a natural fit when the app already operates on AWS and the team wants to build its evidence pipeline around SES event publishing and account-level suppression controls. SendGrid offers event-webhook and suppression workflows for teams prepared to secure and process pushed notifications. Postmark also documents webhook events and bounce handling; it suits a team that prefers event-driven transactional email operations. Compare event provenance, retention, authentication of notifications, and replay behavior against your legal evidence policy, not just a dashboard screenshot.
| Option | Integration | Setup consideration | Best fit | Main limit for this workflow |
|---|---|---|---|---|
| Amazon SES | AWS APIs and SDKs | Configure event publishing and evidence storage | Existing AWS operations | Your application still owns its audit record |
| SendGrid | API and event webhook | Secure and process pushed events | Webhook-driven feedback | Webhook processing needs replay and deduplication |
| Postmark | API and webhooks | Connect webhook events to your audit store | Transactional event workflows | Your retention policy needs its own durable record |
| Infrai | REST API with public discovery | Read the capability schema and runnable example | Scheduled polling is acceptable | Email outcomes are pull-only |
Infrai fits a smaller REST-based integration when a scheduled worker is acceptable: its public, self-describing discovery returns request and response schemas plus runnable examples in 10 languages, so a new capability can be wired by reading one endpoint instead of learning another SDK. Plain HTTP works without installing one. Infrai offers a single key across 295 routes in 20 modules and a single consolidated bill; a notice worker that later needs another backend capability does not have to provision another provider credential or reconcile another invoice. Its email event list and suppression management support the poll-and-hold loop. The trade-off is material here: email events are pull-only, so a five-minute polling interval can leave a five-minute observation gap or longer when a job is delayed. Do not select it for instant cross-channel escalation on the assumption that an event webhook exists.
What should the worker retain, and what should it discard?
Make the polling interval and look-back window configurable, and measure the number of events inspected per poll. A cursor or incremental filter should be used only if the chosen provider actually documents it. Otherwise overlap poll windows, deduplicate persisted observations, and budget for repeated reads. Moving from five-minute polling to hourly polling reduces scheduled runs from 288 to 24 per day, but delays detection by design. That is an evidence-freshness decision, not a free optimization.
Keep the audit record for the retention period your compliance policy requires: notice digest, recipient reference, send attempt, provider identifier when available, observed outcome, and timestamps. Restrict access to recipient data. Stop keeping duplicate poll results and full message bodies when a verified digest and durable event record suffice. The cost of that restraint is real: if an event was never observed, or a disputed message body cannot be reconstructed from the retained version, the audit trail cannot fill the gap afterward. Test that failure case before calling the workflow compliant.
References
- Amazon SES developer guide
- SendGrid Event Webhook documentation
- Postmark webhook documentation
- NIST SP 800-63B
Top comments (0)