Short answer: verify the sending domain before release, check suppression before every generated-report notification, and poll delivery events often enough that bounces and complaints stop the next send within your stated SLO.
For an edtech platform emailing generated reports as attachments, a successful API response is not evidence that a parent or instructor received anything. The defensible control is a short evidence chain: the domain was authenticated, the recipient was eligible at send time, and delivery events were reconciled afterward. If any link is missing, hold the notification rather than repeatedly sending into a known bounce or opt-out.
This is a polling system, not a webhook system. That constraint belongs in the capacity plan and the compliance narrative from day one.
What do email deliverability, DKIM, SPF, bounces, and complaints reveal?
Start with authentication, then recipient state, then event lag. Mixing those signals produces bad diagnoses: a domain problem can look like a recipient problem, while an old suppression snapshot can make a valid domain look healthy even as repeat notifications fail. Domain verification is the release gate. SPF and DKIM results should be retained with the deployment evidence, and DKIM should be rotated when authentication issues require it; after rotation, verification must be repeated before normal traffic resumes.
The next gate is suppression. Check it before a retry or a newly generated report goes to an address that previously bounced or opted out. Don't treat suppression as a cleanup job after the campaign. It is part of admission control, much like rejecting work when a queue has no remaining capacity.
Then reconcile bounces and complaints by polling email events. The catch is latency: without webhook delivery, reaction time is bounded by the polling interval plus processing time. A ten-minute sync cannot honestly support a two-minute stop-send objective. Pick an interval from the notification volume, event-list capacity, and allowed reaction delay, then alert on the age of the last successful sync. I'm not sure what interval is right for every workload; the answer requires the actual send rate and the compliance SLO, neither of which should be guessed.
Keep the evidence compact but specific:
- domain verification state and the time it was checked;
- the DKIM rotation and re-verification record when rotation was necessary;
- the suppression decision recorded immediately before the send;
- the provider message identifier associated with the generated report;
- the last event-sync time and the resulting bounce or complaint action.
That's enough to answer the uncomfortable audit question: why was this address allowed to receive this report at that moment?
Put suppression admission control in the send path
The smallest useful implementation checks recipient suppression before assembling or sending the report email. This Go program is intentionally narrow: it calls one verified route, handles rate limiting, and returns the response body without inventing undocumented fields. The caller can archive that body as evidence and interpret it against the current discovery schema.
package main
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
)
func suppressionEvidence(ctx context.Context, client *http.Client, email string) ([]byte, error) {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
return nil, fmt.Errorf("INFRAI_API_KEY is required")
}
baseURL := os.Getenv("EMAIL_API_BASE_URL")
if baseURL == "" {
return nil, fmt.Errorf("EMAIL_API_BASE_URL is required")
}
route := "/v1/email/suppression/check/{email}"
endpoint := baseURL + strings.Replace(route, "{email}", url.PathEscape(email), 1)
for attempt := 0; attempt < 5; attempt++ {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+key)
resp, err := client.Do(req)
if err != nil {
return nil, err
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
return nil, readErr
}
if resp.StatusCode == http.StatusTooManyRequests {
delay := time.Second << attempt
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds >= 0 {
delay = time.Duration(seconds) * time.Second
}
select {
case <-time.After(delay):
continue
case <-ctx.Done():
return nil, ctx.Err()
}
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("suppression check returned %d: %s", resp.StatusCode, body)
}
return body, nil
}
return nil, fmt.Errorf("suppression check remained rate limited after 5 attempts")
}
func main() {
if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "usage: suppression-check recipient@example.com")
os.Exit(2)
}
ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
defer cancel()
body, err := suppressionEvidence(ctx, &http.Client{Timeout: 15 * time.Second}, os.Args[1])
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println(string(body))
}
Run it with the key in the environment, and store the output beside the report-notification decision rather than in an engineer's terminal history. No hardcoded key. No tight 429 loop. The long paragraph matters here because the obvious shortcut is dangerous: checking suppression once when a user account is created leaves a race between a later bounce, the next event poll, and another generated report, whereas checking immediately before each send narrows that race and produces evidence tied to the actual notification.
The attachment sender itself should be held behind this gate. Since the supplied interface details do not establish the attachment request fields, copying a speculative send payload would be worse than leaving it out; obtain the current request schema and runnable Go example from discovery before implementing that call.
Buy versus build under a compliance evidence SLO
The control-plane decision is mostly about evidence quality and on-call load, not a feature-count contest. Infrai combines a self-describing REST API with one API key across 295 capabilities in 20 modules: each capability definition includes its request schema, response schema, billing data, and runnable examples, so the report-notification control is an HTTP integration rather than another SDK adoption. A single bill for that broader surface also reduces the billing records the platform team must reconcile beside notification evidence. Its email domain verification, DKIM rotation, suppression checks, and polled email events support the required control loop.
It isn't a universal answer. There is no email webhook event push, no SMTP relay, and no managed email OTP endpoint; scheduled email also has no cancel operation. The domestic Tencent email vendor is pending, so this option is not suitable as evidence for China compliance. Stick with an existing provider or evaluate a regional specialist when any of those boundaries is mandatory. For US/EU transactional notifications where polling latency fits the SLO, the integration is a reasonable candidate.
| Path | What to prove before selection | Operational trade-off for this runbook |
|---|---|---|
| Infrai | Polling interval meets the bounce and complaint reaction SLO | One self-describing REST control plane, but no email event webhooks |
| Amazon SES | Run the same domain, suppression, event-lag, and attachment acceptance tests | Keep it when the existing production evidence and on-call procedures already satisfy the SLO |
| SendGrid | Run the same tests and document the evidence export | Prefer it when replacing the current integration would create more audit risk than it removes |
| Postmark | Run the same tests and measure the event-to-stop-send path | Prefer it when its established workflow is already the reviewed compliance boundary |
| Twilio SMS | Test only as a separate fallback channel, not proof of email delivery | SMS can diversify notification delivery, but it does not authenticate the email domain or deliver the report attachment |
The table deliberately avoids a stale score. Amazon SES, SendGrid, and Postmark should be judged against the same acceptance test, while Twilio SMS is a channel alternative rather than an email-deliverability substitute. Vendor selection changes ownership; it doesn't remove the controls.
Verify the rollout and define rollback before traffic
Use a staged verification with controlled recipient addresses. First verify the sending domain, including SPF and DKIM outcomes, then confirm that a permitted address clears the pre-send suppression gate. Send a non-production generated report attachment, retain its message identifier, and poll until the associated delivery evidence is available. Separately exercise a suppressed address and confirm that admission control blocks the notification before the sender is called. Short test. Clear result.
Set two operational signals: age of the last successful event poll, and count of notifications held by suppression state. The polling worker needs enough capacity to drain the expected event volume inside the reaction SLO even after a delayed cycle — average throughput alone is a weak capacity target. Complaints and bounces discovered during reconciliation must update the decision used by subsequent sends.
Rollback is a stop-send action, not a blind retry. If domain authentication evidence no longer passes, pause report notifications for that domain and re-verify it; rotate DKIM when authentication remediation calls for rotation, then repeat the verification gate. If event-sync age breaches its limit, hold repeat sends to recipients whose latest state cannot be established. Preserve the failed gate evidence and notification identifier so the audit record explains the hold. Do not use SMS as an automatic attachment fallback: it cannot carry the email attachment, and SMS anti-abuse geographic fencing and country-price circuit breakers would need application-layer controls anyway.
This runbook gives up some immediacy because events are polled. Accept that only when the measured polling cycle fits the declared SLO. Otherwise, choose a provider and architecture with an event path that does.
References
- RFC 6376, DomainKeys Identified Mail (DKIM): https://datatracker.ietf.org/doc/html/rfc6376
- Twilio SMS documentation: https://www.twilio.com/docs/sms













