For a password reset, the simplest email deliverability service is an API-only service with custom domain setup, explicit bounce handling, and an independently expiring reset token. That boundary keeps a delayed email from becoming a valid credential.
Short answer: verify the custom sending domain with SPF, DKIM, and DMARC; submit one idempotent message through an HTTP API; accept bounce events through a signed webhook or recoverable event feed; and let the application decide when a recipient is suppressed. Measure the whole chain before choosing a service.
The important decision is not which interface has the shortest quick start. It is whether the system can answer one uncomfortable question: did a late password-reset message remain harmless? A service that provides SMTP, an API, or both can still leave that answer to the application.
Late mail is still mail.
What must remain true after the reset link expires?
Start with the security invariant, then choose the mail integration. The reset token should be single-use, bound to the account, and rejected after its short validity window. NIST's digital identity guidance treats recovery and authenticator changes as security-sensitive events; the mail message is only one delivery step in that flow.
The message record needs its own expiry. I use a notification ID, account reference, token version, expiration timestamp, sending-domain revision, and delivery state. I keep the token out of ordinary delivery telemetry. A provider message ID can correlate transport events without making the event store another copy of a credential.
That distinction is easy to miss during a happy-path test. A 2xx response from a mail API means the request was accepted for processing. It does not mean the recipient's mailbox accepted the message, and it certainly does not extend the token lifetime.
The first useful test is therefore a delayed-event test: create a reset request, allow the token to expire, then deliver or replay the corresponding event. The application must refuse the token even if the delivery record later changes from pending to delivered.
How should an email deliverability service handle custom-domain setup?
Domain authentication is part of the delivery contract, not a branding task. SPF publishes authorized senders through DNS. DKIM signs selected message content, and DMARC checks alignment and applies a domain policy. These mechanisms answer different questions, so a passing check for one record is not proof that the whole From domain is aligned.
SPF also has a 10-DNS-lookup limit. That means a record can pass in a small test topology and become fragile after more sending services are added. Keep the SPF policy deliberate. For DKIM rotation, publish the replacement selector before using it and retain the previous selector during the transition window. Record the checked value and verification time so a failed reset campaign can be tied to a domain revision.
I would make domain verification a deployment gate with three outcomes: verified, pending, and rejected. A pending domain must not silently become the production sender. The fallback should be an operationally explicit route, not a surprise change to the visible From address.
How should the API boundary handle retries and bounces?
The password-reset service owns intent; the delivery service owns transport attempts. Give every request a stable idempotency key derived from the notification ID. Do not derive it from the account ID alone, since two legitimate resets for one account can occur at different times.
Here is the small interface I want the application to own. It is deliberately provider-neutral; the event names are an internal contract, not claims about a particular API.
type DeliveryState =
| "created"
| "submitted"
| "accepted"
| "temporarily_failed"
| "permanently_failed"
| "suppressed";
type ResetNotice = {
notificationId: string;
accountId: string;
expiresAt: string;
sendingDomainRevision: string;
};
type DeliveryEvent = {
notificationId: string;
providerMessageId?: string;
state: DeliveryState;
occurredAt: string;
reasonCode?: string;
};
async function submitResetNotice(notice: ResetNotice): Promise<void> {
await mailApi.send({
idempotencyKey: notice.notificationId,
metadata: {
notificationId: notice.notificationId,
expiresAt: notice.expiresAt,
domainRevision: notice.sendingDomainRevision,
},
});
}
The submission result is one event in the state machine. A temporary failure may be retried under a bounded policy. A permanent failure should stop retries and may add the address to a suppression set, subject to the reason. A replayed webhook must be harmless: deduplicate by its provider event identifier when available, then enforce the notification state transition again in the application.
Do not convert every bounce into a permanent suppression. Enhanced mail status codes distinguish temporary conditions from permanent ones, and the original provider reason is useful when an operator reviews a blocked account. Store a small normalized category alongside the original code. The rule should be explainable: this address was suppressed because of a confirmed permanent failure, not because a dashboard happened to see one delayed event.
Which evidence should decide between API-only services?
Evaluate capabilities at the boundary, not by counting integration features. The service should expose custom-domain verification evidence, a stable submission reference, authenticated event delivery, and a recovery path when a webhook is unavailable. The application should retain its own state transitions and suppression decisions.
The simplest useful comparison has three interface shapes:
| Interface shape | Good fit | Boundary to verify |
|---|---|---|
| HTTP API with event webhook | A service that wants explicit application control | Signature validation, replay handling, and event recovery |
| HTTP API with polling or export | Environments that cannot receive inbound webhooks | Event freshness, pagination, and retention |
| SMTP relay plus event controls | A legacy transport boundary already owned by operations | Correlation between relay acceptance and provider outcomes |
None of these choices proves inbox placement. None removes the need to protect reset-token data. That is the main limitation of an API-only service: it gives the application a clean submission boundary, but it cannot turn an accepted request into proof of mailbox delivery. An API-only design is attractive when the application already owns authentication and can keep transport concerns outside the request handler. SMTP remains reasonable when a controlled relay is a deliberate boundary, but adding it merely because it is familiar makes the retry contract harder to see. The trade-off is poor for a team that already audits a relay centrally; it is better suited to an application that needs its own state machine.
Before committing, run the same seven-case rehearsal against each candidate: missing DNS record, invalid signing selector, duplicate submission, delayed event, transient rejection, permanent rejection, and replayed webhook. I care about the resulting state transitions more than a polished setup screen. A candidate passes when the reset token's expiry is unchanged, only one intended message is submitted, and suppression can be explained from retained evidence.
What should telemetry cost in complexity?
Telemetry is part of the threat model. Log notification ID, account reference or a keyed digest, outcome, domain revision, and timestamps. Do not put the reset token, full message body, or raw recipient address into broad dashboards. Full provider payloads belong in restricted storage with a stated retention and deletion rule.
Keep transport latency separate from token lifetime. The former helps diagnose a delivery path; the latter is a security control. Sampling a latency metric may be acceptable at high volume. Sampling a permanent-bounce event is not acceptable if the result drives suppression.
My decision rule is compact: choose the API boundary that preserves the least sensitive evidence needed to explain a reset, supports domain rotation without a hidden sender change, and makes delayed delivery harmless by construction. Test that rule with real DNS records and failure events before optimizing for convenience. In 2026, that is a more durable criterion than a feature checklist.
References
- RFC 7208, Sender Policy Framework (SPF): https://datatracker.ietf.org/doc/html/rfc7208
- RFC 6376, DomainKeys Identified Mail (DKIM) Signatures: https://datatracker.ietf.org/doc/html/rfc6376
- RFC 7489, Domain-based Message Authentication, Reporting, and Conformance (DMARC): https://datatracker.ietf.org/doc/html/rfc7489
- RFC 3463, Enhanced Mail System Status Codes: https://datatracker.ietf.org/doc/html/rfc3463
- RFC 5321, Simple Mail Transfer Protocol: https://datatracker.ietf.org/doc/html/rfc5321
- NIST SP 800-63B, Digital Identity Guidelines: https://pages.nist.gov/800-63-3/sp800-63b.html
Top comments (0)