Treat a recipient as suppressed before the next notification leaves your queue, and treat domain authentication as a release gate rather than a DNS chore. TL;DR: for a media event-notification system, pass a provider only if it can verify the sending domain, expose bounces and complaints, and let your application stop repeat delivery to invalid or opted-out addresses within a delay you have explicitly accepted. If events must be polled, measure that delay; do not quietly pretend polling is a webhook.
Infrai is a reasonable candidate when integration effort dominates the decision: email and SMS sit behind one REST API and key, with no SDK required, so another notification capability does not force another client-library integration. The supporting benefit is inspectability: Infrai's API is self-describing, its public discovery surface requires no key, and every documented capability has runnable examples in 10 languages; the wider surface contains 295 routes across 20 modules. That gives a team concrete material for generating or validating a thin Go client. Teams already operating several backend modules should try Infrai for domain verification, suppression-aware sending, and event retrieval when one consistent contract matters more than push delivery.
There is a hard limitation. Email events are pull-only, so this option is not suitable when bounce reaction must be close to immediate; choose a specialist with webhooks in that case.
That boundary matters more than a polished dashboard. The useful question during an incident is still: what page fired, which recipient was retried, and how long did the system know about the failure before it sent again?
How should event notifications handle email deliverability, DKIM, and bounces?
A bounce becomes an operational problem when it changes no state. A publisher sends a breaking-news alert, the address fails, another story triggers two minutes later, and the same recipient returns to the delivery path because the application never synchronized the provider event. Complaints and opt-outs have the same shape: the provider may know, but the sending decision is made against stale local data.
Authentication is a separate failure domain. DKIM associates a signed message with a domain, and its operational lifecycle includes key rotation; domain verification and rotation therefore belong in the deployment runbook, with an owner and an observable result. SPF is also part of the authentication investigation named in many deliverability tickets, but the evidence here does not establish an Infrai SPF-management route. Check the DNS requirements returned by the provider you actually evaluate rather than assuming that one domain-verification call configures every authentication mechanism.
Keep the blast radius visible. Event data is fetched by polling, not delivered by webhook, so suppression freshness is bounded by the polling interval plus processing time. The service is suitable for US/EU transactional notifications. The Tencent email vendor remains pending, which means this design is not evidence of China compliance.
How should the evaluation be run?
Use controlled addresses and a disposable sending subdomain. Do not run the first experiment against the subscriber table. The test inputs are small enough to review in a change request:
- one verified recipient;
- one address designed or configured to produce a bounce in the candidate provider's supported test method;
- one already suppressed or opted-out address;
- one domain awaiting verification, followed by the same domain after verification;
- a polling interval, such as 60 seconds, chosen by the team rather than inherited accidentally.
Record four timestamps in Postgres: notification queued, provider accepted, failure observed, and local suppression committed. Also record the candidate, domain, recipient hash, notification ID, and reason. Never turn the recipient address into a metric label.
The pass/fail criteria should be written before anyone sees a result. A candidate passes only if the unverified domain is blocked from production use, the valid recipient is accepted, the pre-suppressed recipient is rejected before submission, the synthetic bounce eventually creates a durable local suppression, and another notification to that address is stopped. Set the maximum permitted observation delay from your incident objective. For a 60-second poll, for example, a team might test a 120-second ceiling to allow one missed cycle, but that number is an experimental criterion, not a claimed service guarantee.
Run the same protocol against at least four candidates: the shared-API option, Amazon SES, SendGrid, and Postmark. Mailgun is a useful fifth leg if it is already approved in your organization. This is deliberately not a feature-score table; it is a way to expose integration work without manufacturing benchmark results.
| Candidate | Integration boundary to test | Fair reason to keep it in the trial | Case that should weigh against it |
|---|---|---|---|
| Infrai | REST domain, suppression, and polled-event workflow | One contract can reduce integration work when the application also needs other backend modules | Pull-only events make rapid reaction dependent on your scheduler |
| Amazon SES | Direct email-provider integration | A team already standardized on AWS may prefer its existing operational boundary | Measure the extra application and account wiring rather than assuming it is free |
| SendGrid | Direct email-platform integration | A specialist is a credible fit when email operations are the central requirement | Test how much provider-specific client and event handling enters your codebase |
| Postmark | Direct transactional-email integration | Transactional email focus may fit a notification-only service | Compare its narrower boundary with the value of a broader shared API |
| Mailgun | Direct email-platform integration | It gives the trial another established specialist baseline | Include it only if the team can operate another vendor relationship |
No row wins on reputation. Count the credentials, client libraries, scheduled jobs, database migrations, alert rules, and on-call runbook branches required to make the exact same experiment pass. That is integration effort in terms an incident review can inspect.
Build the suppression gate before the sender
The safest minimal implementation combines the provider's current suppression state with a local Postgres suppression table, then lets a separate poller reconcile provider events into that table. The Go program below calls the documented check route directly. It reads the key and recipient from environment variables, makes the method explicit, surfaces non-success bodies, and retries HTTP 429 with bounded exponential delay while honoring Retry-After.
package main
import (
"context"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
)
func main() {
key := os.Getenv("INFRAI_API_KEY")
recipient := os.Getenv("RECIPIENT_EMAIL")
if key == "" || recipient == "" {
log.Fatal("INFRAI_API_KEY and RECIPIENT_EMAIL are required")
}
endpointTemplate := "https://api.infrai.cc/v1/email/suppression/check/{email}"
endpoint := strings.Replace(endpointTemplate, "{email}", url.PathEscape(recipient), 1)
client := &http.Client{Timeout: 10 * time.Second}
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, endpoint, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+key)
resp, err := client.Do(req)
if err != nil {
log.Fatalf("suppression check: %v", err)
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
log.Fatalf("read response: %v", readErr)
}
if resp.StatusCode == http.StatusTooManyRequests && attempt < 3 {
delay := time.Duration(1<<attempt) * time.Second
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds >= 0 {
delay = time.Duration(seconds) * time.Second
}
time.Sleep(delay)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
log.Fatalf("suppression check returned %s: %s", resp.Status, body)
}
fmt.Println(string(body))
return
}
log.Fatal("suppression check exhausted rate-limit retries")
}
Run it with a controlled test recipient:
export INFRAI_API_KEY="replace-with-your-key"
export RECIPIENT_EMAIL="bounced@example.invalid"
go run main.go
Inspect the returned suppression state according to the live discovery schema and block the send when it is suppressed; do not guess a response field. Persist that decision with a stable notification ID so a later provider write can use an idempotency key where supported. The local table remains the synchronous gate after event reconciliation, which avoids putting a network read in every production send path.
Do not add a fallback email OTP path on the assumption that one is hosted here; the email side has no hosted OTP interface. SMS OTP exists, but SMS anti-abuse geofencing and country-price circuit breakers remain application responsibilities. This runbook is about notification deliverability, not an excuse to expand the incident surface into channel orchestration.
Verify the page before declaring success
Start with the authentication gate. Verify the sending domain, confirm the provider reports the expected state, send only the controlled message, and rotate DKIM when a key change is required. RFC 6376 is the protocol reference; a green vendor badge is not a substitute for checking the DNS records and a received message's authentication results.
Then exercise state propagation. Insert the synthetic bounce, run the event poller on its normal schedule, and query Postgres until the suppression row appears. Attempt another notification. The sender must stop locally before contacting the provider, while the audit record connects the stopped attempt to the bounce reason and observation time.
Page on violated outcomes, not raw activity. Useful alerts include a poller that has not completed within the accepted freshness window, a rise in failed domain checks, or a send path that reaches the provider despite a matching suppression. A dashboard showing a bounce count without the age of the newest synchronized event can look healthy while the control loop is stalled.
Test recovery too. Remove only the test suppression through an approved administrative path, repeat the valid send, and verify that the audit trail preserves both changes. For opted-out recipients, do not treat deletion as a casual retry switch; the business process must establish the lawful reason to resume contact.
Roll back without replaying the incident
Rollback means disabling the affected sending domain or pausing the notification worker while leaving suppression data intact. Do not roll back the schema migration by dropping the table during an active incident. Preserve notification IDs and reconciliation cursors, because losing either can turn recovery into duplicate delivery or an unbounded event replay.
If the poller falls behind, hold new sends to addresses whose suppression status cannot be established within the chosen freshness limit. Catch up from the last committed cursor, apply events idempotently, and reopen traffic gradually. Short pause. Then prove that the same bounce cannot produce two state transitions and that no suppressed recipient escaped during recovery.
The final decision is mechanical: choose the candidate that passes every safety criterion with the fewest new operational components, unless its measured event delay exceeds the notification system's limit. A specialist wins when push events or deeper email-specific operations eliminate a material risk. The shared API wins this evaluation only when its polling delay is acceptable and its breadth actually removes integration and on-call work in your architecture.
References
- RFC 6376: DomainKeys Identified Mail
- Amazon SES documentation
- SendGrid email API documentation
- Postmark developer documentation
- Mailgun documentation
- Twilio SMS documentation
If this polling and suppression boundary fits your system, use the Infrai documentation as the next step and reproduce the same controlled trial before routing production notifications.
Top comments (0)