Short answer: yes, an email API can replace the SMTP login used by your application for 2FA codes and generated marketplace reports. It does not replace the mail system beyond that submission boundary, and an accepted API request does not prove that a buyer or seller received the message. The least complex reliable design is one internal send operation, one durable message record, and channel-specific adapters whose events feed the same delivery state machine.
The page should fire when a time-sensitive message has stopped making progress, not merely when an API call returns an error. If the on-call sees marketplace_report_delivery_stalled, the alert needs the report ID, recipient domain, message class, attempt count, last known delivery state, and age. Otherwise the dashboard may be green while a seller waits for a report that never arrived. I distrust that kind of green. What page fired, and can the responder act on it without opening three other tools?
1. Can transactional email replace an SMTP relay for 2FA codes?
Start at the incident and work backward. A marketplace creates a seller payout report, attaches it to an email, and submits the message. The submission receives an identifier, yet the delivery record remains in accepted past the service's chosen deadline. That stalled state should page before support learns about it from the seller. For a 2FA code, use a much shorter deadline selected from the code's actual validity window; for a report, use the report-delivery objective. Those thresholds are operational policy, not universal constants.
The critical distinction is between submission and outcome. Record state transitions such as queued, accepted, delivered, deferred, bounced, and complained only when the transport supplies evidence for them. Keep the raw event reference and event time, make event ingestion idempotent, and refuse impossible backward transitions. A dashboard that counts HTTP success as delivery hides the failure that matters.
Submission is not delivery.
An API adapter can replace application-side SMTP credentials because the application submits a structured request instead of opening an authenticated SMTP session. Preserve the boundary: the rest of the mail path still exists, authentication policy still matters, and asynchronous outcomes still need handling. Do not promise inbox placement from a synchronous response. The trade-off is operational, not ideological: an HTTP API commonly gives the adapter a structured request and response boundary, while SMTP lets existing mail-capable software submit through a familiar protocol; either still needs a separate path for asynchronous evidence, and neither can prove inbox placement at submission time.
| Submission option | Useful when | Main limitation |
|---|---|---|
| SMTP relay | Existing software already speaks SMTP and the team can operate credential and response handling | Protocol acceptance alone does not describe the later recipient outcome |
| Transactional email API | The application benefits from structured submission and a transport identifier | The application becomes coupled to an adapter contract and still must ingest later events |
| Internal queue plus either adapter | Report generation and user requests must be isolated from transport delay | The team owns durable state, reconciliation, and worker operations |
2. Treat the attachment as a separate failure domain
A generated report can fail before email submission: generation may not complete, an object reference may point to the wrong report, or the attachment may not match the intended recipient. Those are different incidents from a rejected message, so a single send_failed counter is too blunt.
Split the signals.
Bind the immutable report identifier, recipient account, content checksum, and message identifier in the durable record before enqueueing. The worker should read that record, verify the binding, and then submit. Do not log the attachment or a 2FA code. Log identifiers and states that permit correlation without putting sensitive content into the incident trail.
A small Go interface keeps the application contract independent of submission transport while forcing the caller to retain the transport identifier needed for later events:
package delivery
import (
"context"
"io"
)
type Message struct {
IdempotencyKey string
Recipient string
Subject string
AttachmentName string
Attachment io.Reader
}
type Receipt struct {
TransportID string
}
type Sender interface {
Submit(ctx context.Context, message Message) (Receipt, error)
}
This interface is intentionally narrow. Authentication, retry classification, and event verification belong inside an adapter; durable workflow state belongs outside it. SMTP and HTTP implementations can therefore share the same queue and incident semantics without pretending their protocol responses are identical. Its limitation is equally deliberate: it does not model templates, recipient lists, or transport-specific controls. Add those only at the boundary that needs them, because pushing every transport feature into the shared interface turns a replaceable adapter into a lowest-common-denominator protocol.
3. Separate retryable submission from duplicate business action
Retries are necessary, but blind retries can send the same report or login code twice. Assign an idempotency key to the business action before the first attempt, persist each attempt beneath it, and let only one worker own an active lease. If a request times out after crossing the submission boundary, reconcile by that key or recorded transport identifier before sending again.
Short-lived 2FA codes add another rule: never retry a code after its validity window or after a newer challenge supersedes it. The send path should carry an expiry time and challenge version even if the transport adapter ignores both. A late message is not a success. It is confusing evidence delivered to the user.
Stop there.
For reports, the business action is deliver report version X to account Y, not call send. That wording matters during a postmortem because it exposes duplicates, stale versions, and recipient-binding mistakes that a transport-only graph cannot show.
4. Authenticate the domain and verify event evidence
Changing the application submission method does not remove domain authentication. DMARC publishes a domain policy and evaluates identifier alignment with SPF and DKIM; RFC 7489 also describes aggregate and failure reporting. Roll out policy with measurements from real traffic because forwarding and indirect mail flows can produce results that deserve investigation before enforcement changes.
Inbound delivery events are another trust boundary. Verify their authenticity using the transport's documented mechanism, retain enough metadata to audit the decision, and deduplicate events before updating state. A forged delivered event is worse than a missing graph because it can suppress the only useful page. Event feeds also have a practical limitation: they report what the emitting system knows, which may be acceptance by a downstream server rather than a human opening or acting on the message. Name states after the evidence actually received; do not silently upgrade accepted into received.
SMS fallback has its own transport constraints. GSM-7 messages are segmented after 160 characters, while UCS-2 messages are segmented after 70; concatenated segments have smaller per-segment limits. That means localization or a single non-GSM character can change segmentation. Keep the 2FA text short, test the actual encoded variants, and monitor segment count separately from email delivery.
5. Tune the alert against actionability, not dashboard color
The instrumentation change is straightforward: emit a timestamped transition for creation, report readiness, queue admission, submission acceptance, and terminal outcome; then alert on age in state by message class. Break down the signal by recipient domain and adapter, but keep account identifiers out of metric labels. Use logs or traces for high-cardinality correlation.
The responder should be able to answer three questions from the page: Is generation stuck or transport stuck? Is the failure concentrated by recipient domain or message class? Can retry still satisfy the business deadline? If the alert cannot answer those questions, it is probably a ticket generator wearing a pager badge.
There is a cost to aggressive thresholds. Normal asynchronous delay can page the on-call even though the message later arrives, and high-cardinality slices can turn ordinary variance into apparent incidents. Begin with the business deadline, compare page volume with terminal outcomes, and adjust only with recorded evidence. For 2FA, monitor expiry-relative latency and failed challenges; for reports, monitor stale states and missing terminal events. Different clocks deserve different pages. This is the false-positive tax: a threshold tight enough to notice every harmless delay trains responders to distrust the page, while one wide enough to stay quiet may surrender the useful intervention window. The correct setting cannot be copied from another system; it comes from the message's deadline, observed state durations, and a documented action the responder can still take.
The decision rule is plain: choose SMTP submission or an email API according to the authentication model, event evidence, idempotency support, attachment handling, and failure visibility your team can operate. Either path is acceptable if the workflow owns durable state and alerts on stalled outcomes. Neither path earns trust merely because its submission dashboard is green.
Top comments (0)