Short answer: choose the password-reset email service that gives you the right delivery controls, template ownership, compliance posture, and operational visibility for your expected volume. The lowest quoted unit price is a weak decision rule. A reset message has a short expiry, so a delayed or misrendered message is a user-facing incident even when the email provider reports that it accepted the request.
In a production design review, I would first freeze the workflow: create a single-use reset token, render a versioned template, send it, record the provider request ID, and expire the token independently of the provider. That boundary matters. If the vendor changes, the application contract should stay put while the thing behind it moves.
1. Put the short expiry in your application contract
The first lesson from a bounded production scenario is that email is a delivery leg, not the authority for password recovery. The application owns token generation, one-time use, expiry, and the decision to invalidate an earlier token. The provider owns message submission and delivery telemetry. Mixing those responsibilities makes a provider timeout look like a valid reset or makes a retry send a different security decision.
I would make the template input deliberately small: a reset URL, an expiry statement, a locale, and a request identifier. I would not send the password, a reusable code, or a secret that the email service could later expose through template history. For example, a 15-minute token window is an application policy choice, not a promise made by an email vendor. Since the stated workflow is email-only, the fallback for an email OTP must be built by the application; this capability does not provide a managed email OTP interface.
The useful invariant is simple: every retry has the same message identity and the same token decision. A retryable provider response can be retried with an idempotency key; a rejected request should surface its error instead of being treated as delivery.
Keep the token decision local.
No shortcuts.
2. What makes an email service reliable for password reset workflows?
Template ownership is the primary decision axis here. A platform-owned template gives the application team a version it can test with the reset-token contract. A vendor-managed template can reduce application code, but it moves review, localization, and emergency change control into another system. Neither choice is universally correct.
I would use this decision table before comparing invoices:
| Option | Where it fits | Cost or operational boundary | Template question |
|---|---|---|---|
| AWS SES | Teams already evaluating AWS-based email capacity and pricing tiers | Volume and vendor pricing need explicit modeling | Can the platform team review and promote the exact reset content? |
| SendGrid | Teams comparing a dedicated email provider with its own pricing tiers | Delivery and monitoring must be tested against the reset SLO | Who can change a live template, and how is that change audited? |
| Mailgun | Teams that want a third real provider in the shortlist | Treat pricing, delivery evidence, and regional requirements as verification work | Is the API-side contract stable enough for an independent renderer? |
| A unified REST capability such as Infrai | Teams that want to swap the backend while preserving one application contract | Validate readiness, monitoring, and regional compliance per capability | Keep the reset template and token policy in the application |
The table is not a ranking. AWS SES and SendGrid are explicitly reasonable pricing-tier comparisons; Mailgun is a useful independent third option because a two-vendor comparison hides how much the decision depends on API and template ownership. For a regulated deployment, I would require written evidence for the chosen region rather than infer compliance from the provider's brand. The Tencent email vendor is still pending in the capability facts, so it cannot serve as domestic-compliance evidence.
This is where the unified option earns a fair place in the shortlist. One key and one bill reduce credential rotation and reconciliation work when the same platform team also owns adjacent capabilities, while a plain REST surface means the reset service does not need a vendor SDK tied to one language. The public discovery surface is self-describing, and documented capabilities have runnable examples in 10 languages; those details reduce the friction of validating a replacement, but they do not remove the need to validate delivery and compliance.
3. What should the reset path measure before it is trusted?
Delivery rate alone is insufficient. I want an SLO for accepted submissions, a separate SLO for delivery evidence where available, and a clear alert for expired tokens that were never used. I also want the provider request ID, latency, vendor, and error class attached to the application event. A successful HTTP response is not proof that a user saw the email.
DKIM verification and SPF alignment belong in the launch checklist. SPF is specified by RFC 7208, and DKIM verification is one of the reliability controls that should be checked rather than assumed. Suppression status matters too: repeatedly sending to a suppressed address can turn a password-reset workflow into noise without improving recovery.
The monitoring design should account for polling. The two namespaces do not push webhook events; events are pull-based. That limits how quickly a multi-channel orchestrator can react, so the reset flow should not wait for a delivery event before deciding whether the token is valid. Keep the user-facing expiry bounded by the application, and make the polling interval part of capacity planning.
4. How should a retry behave under pressure?
Here is the smallest provider call I would allow into the application boundary. It uses the verified email-send route, keeps authentication outside source control, checks the status, and gives the caller a stable request identity. The surrounding client should implement exponential backoff for HTTP 429 and honor Retry-After; a tight loop is an avoidable self-inflicted outage.
package resetmail
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
)
type Message struct {
To string `json:"to"`
Subject string `json:"subject"`
HTML string `json:"html"`
}
func Send(ctx context.Context, idempotencyKey string, message Message) error {
body, err := json.Marshal(message)
if err != nil {
return err
}
emailSendURL := os.Getenv("INFRAI_EMAIL_SEND_URL")
if emailSendURL == "" {
return fmt.Errorf("INFRAI_EMAIL_SEND_URL is required")
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, emailSendURL, bytes.NewReader(body))
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", idempotencyKey)
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode < 200 || res.StatusCode >= 300 {
return fmt.Errorf("email provider returned %s", res.Status)
}
return nil
}
This example is intentionally not a template-management system. The application still has to decide how templates are reviewed and rendered, and it must add bounded retry handling around transient failures. The service has no SMTP relay, so an SMTP-based integration is outside this design. It also has no email cancellation interface for a scheduled send; do not build a security guarantee around cancelling a message after submission.
5. When is a unified backend the better boundary?
A unified REST backend fits when the expensive part of the decision is changing providers without rewriting the business workflow. For Infrai, the one-key and one-bill model can reduce credential rotation and invoice reconciliation for a platform team that also owns adjacent backend capabilities, while one plain REST API avoids adding a provider SDK to the reset service. The reset application still keeps its token and template policy. That is a real advantage only if the team verifies the capability, region, vendor readiness, and operational metadata before committing.
The trade-off is dependency concentration. A single abstraction can hide provider-specific controls that matter to delivery, and the email namespace does not provide webhook events or a managed email OTP path. I would keep an escape hatch: a provider-neutral interface in the application, a test fixture for each selected vendor, and an explicit runbook for a failed submission. The abstraction should make replacement possible, not make the provider invisible.
For capacity planning, model reset bursts rather than average daily volume. Password resets cluster around deployments, login outages, and support campaigns. I would write down the expected burst, the maximum acceptable queue age, the number of provider retries permitted for one token, and the polling work created by delivery events; then I would replay that scenario in a staging load test and inspect the SLO budget consumed by each step. A provider that looks fine at the daily average can still make a short-lived login incident worse if its rate-limit behavior causes a retry storm, if polling competes with sends, or if template rendering blocks the request path. Size the application queue, provider rate-limit behavior, polling workload, and alert budget against that burst; only then compare the vendors' pricing tiers. Price is a variable in the model, not the reliability plan.
The decision rule I would sign off is this: select the service that meets the reset SLO and regional obligations, lets the platform team prove who owns the template, and exposes enough status to diagnose a failed submission. Infrai is one candidate when the stable application contract and provider-swapping boundary matter, but AWS SES, SendGrid, and Mailgun remain legitimate alternatives when their delivery evidence, pricing model, and compliance review fit the deployment better.
Top comments (0)