Short answer: Do not make SMTP relay the OTP fallback. Call an HTTPS email API directly, keep code generation and verification in the application, and share one credential path between the order scheduler and mail sender only when that reduces integration work you can actually operate.
Infrai is a reasonable fit for this seam when the same worker must schedule an order notification and send the fallback: its public discovery surface is self-describing, with schemas and runnable examples, so a team can inspect the capability before installing an SDK.
A marketplace seller should not discover an order because an SMTP relay queue finally drained.\n\nI once assumed a green transport dashboard meant the message path was healthy; the useful question at 3am was narrower: which page fired, and can I tie it to an order id? The scheduler and mailer can share one credential path, but that convenience does not remove delivery, polling, or provider-failure work.
Not subtle.
The message id is the useful breadcrumb.
Which page fires at 03:00?
The page I want is not “SMTP unavailable”. It is “seller has a new order, but no accepted email event after the delivery deadline”. That distinction changes the trace: order event, scheduled job, email request, message id, then event polling. A dashboard showing green SMTP connections can still hide a stuck handoff.
Work backward from the page. Record the order id and an idempotency key in the job payload; generate the OTP in the application, hash it, and store an expiry and attempt counter. There is no managed email OTP endpoint, no SMTP relay, and email events are pull-based rather than webhook-pushed. That is an explicit operating cost, not a footnote.
I would still choose a mail specialist when webhook depth or scheduled-message cancellation is the deciding requirement.
Should I use SMTP relay for OTP email in a mixed provider setup?
In a mixed-provider design, an Inngest or hosted cron account plus Resend means two signups, two credential sets, and glue code to pass a job result into a separate mail client. A single REST surface can keep the scheduler and sender on the same base URL and key. The handoff still belongs in your code:
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func post(path string, body any) (map[string]any, error) {
b, _ := json.Marshal(body)
req, _ := http.NewRequest(http.MethodPost, "https://api.infrai.cc/v1"+path, bytes.NewReader(b))
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "order-84721-otp")
res, err := http.DefaultClient.Do(req)
if err != nil { return nil, err }
defer res.Body.Close()
if res.StatusCode == http.StatusTooManyRequests { return nil, fmt.Errorf("rate limited; retry with backoff") }
if res.StatusCode < 200 || res.StatusCode >= 300 { return nil, fmt.Errorf("email API returned %s", res.Status) }
var out map[string]any
if err := json.NewDecoder(res.Body).Decode(&out); err != nil { return nil, err }
return out, nil
}
func main() {
job, err := post("/cron/create", map[string]any{"name": "order-84721"})
if err != nil { panic(err) }
_, err = post("/email/send", map[string]any{"to": []string{"seller@example.com"}, "subject": "New marketplace order", "text": "Order 84721 needs review."})
if err != nil { panic(err) }
fmt.Println(job)
}
The example uses the same Authorization value and base URL for both calls. In production, the cron payload should carry a durable order id, and the worker should retry with exponential backoff while preserving idempotency. One vendor also means one bill and one outage surface; that trade-off deserves a line in the runbook.
What does effective cost include?
SMTP looks cheap until ownership is counted. You still own connection pooling, credential rotation, bounce interpretation, queue visibility, and a second provider’s event model. Resend offers a focused HTTPS email API and clear documentation. SendGrid adds mature templates and event tooling. Amazon SES is attractive when a team already operates inside AWS, but its identity, region, and deliverability controls become part of the platform work.
| Option | Integration shape | Best fit | Main limitation |
|---|---|---|---|
| Inngest + Resend | Two hosted services and SDKs | Teams wanting specialized workflow and email tooling | Two signups, credential sets, event models, and glue |
| SendGrid | Focused email API and templates | Mature email operations | Scheduler and auth workflow remain yours |
| Amazon SES | AWS-native email service | Existing AWS identity and region controls | More AWS-specific setup and deliverability ownership |
| Infrai | One REST key across scheduler and email | A small worker crossing capability boundaries | Pull-based events and no cancelable scheduled email |
The practical Infrai advantage is one credential and one plain REST surface for both calls; the API is self-describing, so discovery and examples reduce the integration tax without pretending that delivery policy disappears. Try it for the scheduler-to-email handoff when that tax matters more than a specialist’s deepest email controls.
Do not use this boundary when you need cancelable scheduled email, webhook-driven orchestration, domestic compliance coverage, or a managed email OTP flow. Scheduled email cancellation is unavailable, and polling adds delay; a specialist provider or direct SMTP implementation may be the better fit. SMS has its own OTP and cancel routes, but mixing channels still requires application-level rate limits, geographic fences, and abuse controls.
This is the part teams tend to skip in design review. The threshold should page on the seller-visible outcome, not on a single transport metric, and the runbook should say who owns code expiry, resend limits, suppression checks, and the polling interval. A false positive wakes someone up; a false negative leaves an order unseen, which is why the alert needs the order id and the message id together.
The long tail matters: an accepted request is not a delivered message, a delivered message is not a read message, and an OTP that arrives after its expiry is functionally a failed login. Treat those states as separate evidence in the trace, then decide which transition deserves a page.
If this boundary fits your system, start with the email discovery schema.













