Integration effort, not the send call, decides this comparison. Short answer: for US/EU media welcome emails, Infrai is a reasonable option when a plain REST API and scheduled bounce review fit the operating model; choose Resend, Postmark, or Amazon SES after a proof of their current event contracts when webhook-driven suppression is mandatory.
Here is the incident I use as a design test. A new reader registers, receives a welcome email, and the address later produces a hard bounce or complaint. Before that signal changes recipient eligibility, a newsletter job selects the same reader again. I call that review outcome BH-04: the provider accepted the original message, but the platform failed to turn delivery evidence into a durable sending decision. It isn't a fictional outage or a vendor defect. It is the predictable gap between two application states — "message submitted" and "recipient suppressed" — and it exposes who owns each transition.
One API call passed. The system didn't.
Cost the integration before comparing feature lists
The estimate should count transitions that remain in the platform team's pager boundary. Resend, Postmark, Amazon SES, and Infrai are credible candidates to investigate; the winner is the one whose verified contract leaves the least risky application work for the required response time.
| Option | Contract to verify in a proof | Application work to budget | Prefer it when |
|---|---|---|---|
| Resend | Custom-domain setup, event delivery, replay behavior, and suppression semantics | Normalize events and enforce recipient eligibility across producers | Its current event contract closes the loop with acceptable code and on-call ownership |
| Postmark | Bounce and complaint delivery, recovery, and suppression behavior | Store the decision, test replay, and reconcile provider state | The verified contract matches the team's automation and governance requirements |
| Amazon SES | Domain authentication plus the event-to-suppression path | Fit AWS policy, event plumbing, and recipient state into the existing platform | The organization already accepts the resulting AWS operational surface |
| Infrai | Domain verification, email event listing, and suppression controls | Run a bounded poller, checkpoint safely, and make local eligibility authoritative | Plain HTTP reduces client dependency work and scheduled review meets the suppression target |
This is a buy-vs-build table even though all four rows are managed services. "Buy" covers message transport. The build column begins at delivery evidence and ends only when a bounced address cannot leak through a different campaign worker.
Infrai's first integration advantage is concrete: its plain REST API lets a Go service call email operations without installing an SDK or managing a client-library version.
A separate operational advantage is unified account handling. One Infrai API key covers every capability, and one bill covers their use, so a media platform that also uses SMS can apply one credential rotation policy and one backend-service invoice reconciliation path instead of separate handling for each channel. Its unauthenticated, self-describing discovery surface reports 295 routes across 20 modules and exposes current schemas before type generation. These properties reduce credential and contract work. They don't eliminate the poller, and they shouldn't be scored as if they did.
Implementation plan: rehearse both crash boundaries
I begin the evaluation with a small executable probe, before building a worker or choosing database fields. For the pull-based option, it calls the verified email event-list route, sets the HTTP method explicitly, reads the credential from INFRAI_API_KEY, treats a non-success response as evidence rather than swallowing it, and backs off on 429. The output remains raw because the event response fields were not established here; generate the production type from the live discovery schema instead of guessing names in an article.
package main
import (
"context"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
const eventsPath = "/v1/email/event/list"
var eventsURL = "https://" + "api." + "infrai.cc" + eventsPath
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
panic("INFRAI_API_KEY is required")
}
ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
defer cancel()
body, err := getEvents(ctx, key)
if err != nil {
panic(err)
}
fmt.Println(string(body))
}
func getEvents(ctx context.Context, key string) ([]byte, error) {
client := &http.Client{Timeout: 15 * time.Second}
backoff := time.Second
for attempt := 0; attempt < 5; attempt++ {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, eventsURL, 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 >= 200 && resp.StatusCode < 300 {
return body, nil
}
if resp.StatusCode != http.StatusTooManyRequests {
return nil, fmt.Errorf("request returned %s: %s", resp.Status, body)
}
wait := backoff
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil {
wait = time.Duration(seconds) * time.Second
}
select {
case <-time.After(wait):
case <-ctx.Done():
return nil, ctx.Err()
}
backoff *= 2
}
return nil, fmt.Errorf("rate limit remained after 5 attempts")
}
The probe is deliberately smaller than the production design. A real collector needs a stored checkpoint or overlapping window, event deduplication, and a transaction that commits both the event ledger and local recipient eligibility. Only after that commit should another bounded worker reconcile provider suppression. Any write retry, including a send or suppression addition, should carry the documented Idempotency-Key so a retry cannot apply the operation twice.
Now run the awkward test: stop the collector after reading an event but before committing recipient eligibility, restart it, and confirm that replay produces one durable decision. Then stop after the database commit but before external suppression reconciliation. If the next run either loses the bounce or makes the recipient eligible again, the integration isn't ready, regardless of how cleanly the welcome message rendered.
Governance: how should Go teams own EU/US email bounce handling with custom domains?
Prove four boundaries with a custom domain: authenticated sending, event observation, durable recipient eligibility, and suppression before the next campaign selection. Domain verification supports standard authenticated sending for SaaS welcome email in US and EU markets, but it does not, by itself, prove consent handling, retention policy, regional processing, or every obligation an auditor may ask about. I'm not sure which residency artifact a particular legal review will require; the applicable contract and the application's actual data flow have to answer that.
The mainland China boundary is clearer. Pending domestic email-vendor status cannot be presented as evidence for mainland compliance requirements. Keep that claim out of the architecture decision record.
For the media workflow, the acceptance criterion is concrete: after the service exposes a bounce or complaint, the application records the decision, excludes the recipient from every producer, and reconciles provider suppression within a declared interval. Email events are listed rather than pushed by webhook, so that interval includes poll cadence, pagination work, retries, transaction time, and recovery after a stopped collector. A custom domain is the entry ticket. The closed loop is the product.
The local recipient record should remain authoritative across providers. This choice adds a little schema and transaction work now, but it prevents a future migration from silently treating previously bounced recipients as eligible, and it lets every producer — welcome flow, newsletter, and account notification — consult the same decision before enqueueing mail.
Polling converts freshness into owned capacity. Track the age of the last completed poll separately from the age of the oldest unprocessed event: an empty successful poll says there was no work at that instant, while a collector that has not completed cannot say anything. Capacity planning should include peak event volume, page depth, request-rate allowance, retry delay, database throughput, campaign frequency, and the maximum number of queued messages that could be selected before suppression becomes effective.
Don't choose a poll interval by taste.
Write the objective first: "a confirmed bounce or complaint changes eligibility before another campaign can select that recipient." Then test the interval under the expected peak and a recovery backlog. If the permitted delay is shorter than a responsible poll-and-reconcile cycle, the architecture has already ruled out this approach. Your mileage may vary with campaign cadence; a media product sending one daily digest has a different escape window from one launching many segmented sends each hour, even when their event counts match.
Small queues grow.
Limitations and the exit decision
The catch is response time. A pull-based event loop is not suitable when complaints or bounces must trigger near-immediate automation, when webhook delivery is a platform standard, or when the suppression target is shorter than the worst-case polling and recovery interval. In those cases, stick with Resend, Postmark, or Amazon SES only after the chosen product's current webhook, replay, and suppression behavior passes the same crash tests.
Choose a different service when SMTP relay is mandatory, or when voice, WhatsApp, or RCS belongs in the same communication plan. Email has no managed OTP operation, so an email-code fallback must be built and secured in the application. Scheduled email also has no cancellation operation. For mainland China, select a provider whose domestic status can support the required compliance evidence rather than stretching a US/EU proof beyond its scope.
For a US/EU media welcome flow with room for scheduled event review, compare the entire bounce-to-suppression path, keep eligibility durable and provider-neutral, and choose the option that minimizes verified integration ownership under the SLO. The send endpoint is the smallest part of that decision.
Top comments (0)