The email deliverability monitoring page says compliance_notice_delivery_gap: marketplace notices accepted during the last hour have no terminal delivery record. Which API should on-call use to separate a bounce or complaint from stale event ingestion? They need a correlation ID, the affected recipient domain, the current suppression decision, and a safe way to determine whether notices were rejected, delayed, or merely missing from the event stream.
The least complex sound design is an email API that pushes signed, replayable bounce and complaint events, exposes suppression state, and reports domain authentication health; use polling for health and reconciliation, not as the primary delivery signal. Keep the compliance template in the application when legal must approve the exact bytes and retain a versioned audit trail. The provider should transport the notice and report facts about its disposition, while the application owns why it was sent, which template version produced it, and what evidence was recorded.
This split matters more than a long feature checklist. An API can have excellent sending throughput and still be a poor fit if its event contract cannot connect a marketplace transaction to a delivery outcome without storing raw recipient data in every log.
Start with the audit record, not API features
Choose against the evidence path, starting at the page and working backward. A transactional email API should accept an application-generated message ID or metadata token, return a provider message ID, authenticate event callbacks, distinguish temporary from permanent delivery failures, expose complaint events, and let the application query suppression state. Domain health belongs on the same operational map, but it runs at a slower control-plane cadence: authentication status and aggregate domain signals do not need to block every send.
Do not reduce this to "does it have webhooks?" A callback with no stable event ID cannot be deduplicated. A callback with no replay or reconciliation path makes a missed delivery indistinguishable from a delayed one. A suppression endpoint with no reason and timestamp cannot explain a decision to support or compliance staff. For each candidate, ask for the event schema and retention contract before looking at the sending SDK. The SDK is replaceable; an ambiguous audit record is not.
For a marketplace compliance notice, the durable record can be compact: internal notice ID, account ID, template version, purpose, requested timestamp, provider message ID, normalized disposition, provider event ID, event timestamp, and a hash of the rendered content. Keep recipient addresses out of high-cardinality metric labels. Where an address is required for suppression handling, store it under the same access and retention controls as other personal data. GDPR Article 5 includes purpose limitation, data minimization, storage limitation, and accountability; Article 28 governs processor terms. Those are design inputs, not paperwork to bolt on after launch.
I would evaluate the contract with a trace like this:
| Step | Required evidence | Failure that should page |
|---|---|---|
| Render | Template version and content hash | No approved version for the notice type |
| Submit | Internal and provider message IDs | Sustained submission failure threatens the notice SLO |
| Event ingest | Signed event ID and event time | Event freshness or reconciliation gap breaches its budget |
| Normalize | Delivered, transient failure, permanent failure, complaint | Unknown dispositions exceed a defined budget |
| Suppress | Reason, source, and decision time | A permanent failure or complaint is eligible to send again |
| Reconcile | Counts and unmatched message IDs | Submitted records remain unexplained past the delivery window |
The exact SLO must follow the legal delivery obligation and the business workflow. Do not invent a "five nines" target because it looks serious. Define the notice window, the acceptable fraction with a known terminal state, and the maximum event age; then page only when remaining error budget and time-to-deadline justify waking someone. I'm not sure a single target works across every marketplace: a same-day account restriction and a monthly policy reminder have different clocks. Legal and product owners must resolve that before engineering can set a defensible threshold.
How should email deliverability monitoring use bounce, complaint, and suppression events?
Suppose the page contains only a falling delivery rate. That signal fires late and mixes several causes: mailbox rejection, authentication trouble, a complaint-driven suppression, event ingestion lag, or a change in recipient-domain mix. The earlier signal is usually a broken evidence pipeline, not a raw delivery percentage. Track submission-to-first-event latency, terminal-state completeness by notice cohort, callback signature failures, duplicate event rate, unknown enhanced status codes, and the age of the oldest unreconciled submission.
Enhanced mail status codes provide a useful normalization layer. RFC 3463 defines persistent classes such as 5.1.1 for a bad destination mailbox address while the first digit distinguishes success, persistent transient failure, and permanent failure. Preserve the provider's original diagnostic in restricted logs, but drive automation from a small internal taxonomy. A permanent recipient failure can update suppression state; a transient failure should remain visible without being mislabeled as a complaint. Complaint reports are a separate signal, standardized in the Abuse Reporting Format described by RFC 5965.
Instrument cohort completeness rather than one global counter. Group by notice type, template version, sending domain, and recipient-domain class, with bounded labels. Then record a histogram for event delay and counters for each normalized disposition. The long paragraph is intentional because the interaction is where alerts go wrong: if a new template version is released to ten percent of notices and causes authentication alignment or content-policy trouble at one large mailbox domain, a global delivery-rate alert averages away the change; if the alert splits on every recipient domain, the pager becomes a cardinality meter. Use a controlled set for large domains, aggregate the tail, and attach full dimensions to sampled traces or audit queries instead of metrics.
Keep it bounded.
A small Go normalizer makes the policy explicit and testable:
package delivery
import "strings"
type Disposition string
const (
Transient Disposition = "transient_failure"
Permanent Disposition = "permanent_failure"
Delivered Disposition = "delivered"
)
func Normalize(status string) Disposition {
status = strings.TrimSpace(status)
switch {
case strings.HasPrefix(status, "2."):
return Delivered
case strings.HasPrefix(status, "4."):
return Transient
default:
return Permanent
}
}
The production parser should reject malformed values into an explicit unknown bucket rather than silently treating them as permanent failures. Test it with the actual event fixtures supplied by a candidate API, including duplicates and delivery out of order. Don't let a provider-specific string leak into application policy.
Who should own compliance notice templates?
Application-owned templates fit compliance notices when legal approval, deterministic rendering, and version retention dominate convenience. Persist the immutable template version beside the notice record and deploy content through the same review controls as code or policy. A rollback then means selecting a previously approved version, not editing a remote template in place. The catch is that the application team owns rendering correctness, localization, preview tooling, and safe handling of substitutions. That workload is real.
Provider-owned templates can be the better choice when non-engineering teams change routine transactional copy frequently and the provider offers an approval model that satisfies the organization. Stick with that model when centralized content operations matter more than byte-for-byte reproducibility inside the application repository. It is not suitable when the audit must prove exactly which locally approved artifact generated a notice and the remote system's version history or export controls do not meet that requirement.
The send boundary should still carry an immutable template version and notice ID, regardless of where rendering occurs. For one-click unsubscribe, RFC 8058 specifies a List-Unsubscribe-Post header and describes an HTTPS POST mechanism. Whether that belongs on a compliance notice depends on the legal purpose and message classification; engineering should not infer consent policy from a protocol feature. Transactional does not mean exempt from every jurisdictional or mailbox rule.
Preserve a migration boundary while choosing the control plane
Capacity planning starts with event amplification. One submitted message may produce acceptance, delivery, delay, bounce, or complaint activity, and retries can duplicate callbacks. Size the queue and audit store from peak submission rate multiplied by the candidate's documented maximum event and retry behavior, then test a replay burst. Don't derive queue capacity from average daily volume. Reserve headroom for a large marketplace batch and for reconciliation running at the same time.
| Decision | Buy a managed email API when | Build or self-host more when |
|---|---|---|
| Delivery network | Mailbox relationships and operational coverage are outside the team's charter | The organization can staff reputation, abuse, routing, and on-call ownership |
| Event evidence | Signed events, stable IDs, replay, and export meet audit needs | Required evidence cannot be exported or retained under policy |
| Templates | Remote governance matches legal approval | Local immutable artifacts are mandatory |
| Data location | Processing terms and regional controls match US and EU flows | Architecture requires controls the service cannot contractually provide |
| Lock-in | A normalization adapter and export path bound migration cost | Provider concepts have become application policy |
This is not a binary forever-decision. Keep a narrow transport interface, normalize delivery events at ingestion, and archive enough provider-neutral evidence to reprocess them. Avoid promising that switching providers needs no code changes; authentication, event semantics, suppressions, warming, and compliance reviews make migration a project even behind a clean interface.
Polling still has a job. Use it to check domain authentication configuration, reconcile submitted IDs that have no event after the expected window, and verify suppression state before sensitive retries. Apply jitter and conditional requests when the API supports them, and establish a request budget from the number of domains and the polling interval. Event delivery should carry fast-changing message state because per-message polling scales with outstanding mail and creates detection lag.
US SMS is a separate compliance surface. If a fallback sends a text, Twilio's A2P 10DLC documentation describes registration requirements for application-to-person traffic over US 10-digit long codes. Do not assume an email consent record, suppression policy, or delivery event schema transfers to SMS. Model the channel, legal basis, destination, and evidence independently, then join them under the internal notice ID.
Spend the false-positive budget deliberately
Close the trace at the pager. A threshold on delivery rate alone will fire during benign recipient-mix changes and stay quiet when events stop arriving but the last known rate looks healthy. Alert first on terminal-state completeness and event freshness relative to the compliance deadline, then use bounce, complaint, suppression, and domain-health signals to route diagnosis. A burn-rate style alert can combine urgency with sustained impact, but its windows and budgets must come from the notice SLO.
Every page consumes attention. Set a ticket-level threshold for slow domain-health drift, a page for imminent audit-record breach, and a separate security response for invalid callback signatures. Review false positives after each threshold change, but do not tune away an unexplained gap merely because it is noisy. The right API is the one that lets the team make those distinctions with attributable evidence while keeping template ownership where governance needs it.
References
- https://datatracker.ietf.org/doc/html/rfc3463
- https://datatracker.ietf.org/doc/html/rfc5965
- https://datatracker.ietf.org/doc/html/rfc7489
- https://eur-lex.europa.eu/eli/reg/2016/679/oj
Top comments (0)