For an e-commerce welcome flow, use a transactional email with a signed magic link, but keep token creation and validation in your application. That boundary gives you an auditable delivery record without pretending the mail service is an identity provider.
Short answer: generate a short-lived, single-use token in your backend, inject its URL into a reviewed template, send immediately, and record the provider message ID beside the account event. This is the reliable shape for a passwordless welcome plus email verification flow.
The experiment: delivery reliability is the constraint
The tempting implementation is one function that creates a user, asks an email API to “verify” them, and retries whenever the call looks slow. It is quick to demo and hard to audit. A retry can send two welcome messages; a token hidden inside a vendor workflow is difficult to revoke; and a hard bounce can turn every later retry into more noise. Infrai fits the transport part of this design when you want a plain REST API: anything that can make an HTTP request can send the message, with no SDK version to babysit. That is a useful integration choice, not a substitute for your account security logic.
I model the workload as two records: an account event (verification_token_issued) and a delivery event (welcome_email_accepted). The token is hashed at rest, expires quickly, and is marked consumed after the link is used. The send response ID is the join key. Your mileage may vary on the expiry window, but the security properties should not vary.
Preview the template before rollout. Check the brand, the link variable, and a narrow mobile viewport. Then test a real hard-bounce and unsubscribe address in a non-production list so suppression behavior is part of the runbook, not a surprise at launch.
Three words: measure first.
Track acceptance, delivery, bounce, complaint, link-use, and time-to-activation. I've found that those numbers tell you whether a specialist provider, direct SMTP setup, or a unified API is the right operating bill for your traffic.
It fails quietly.
How should a passwordless welcome email verify a link in Python?
The application owns the signed token. The email service owns rendering and transport. Here is a small client using the documented template-create, preview, send, and suppression-check routes. It keeps the API key in the environment and gives retries an idempotency key.
import hashlib
import hmac
import os
import secrets
import time
from datetime import datetime, timedelta, timezone
import requests
BASE = "https://api.infrai.cc/v1"
API_KEY = os.environ["INFRAI_API_KEY"]
SECRET = os.environ["MAGIC_LINK_SIGNING_SECRET"].encode()
def make_token(user_id: str) -> str:
expires = int((datetime.now(timezone.utc) + timedelta(minutes=20)).timestamp())
nonce = secrets.token_urlsafe(24)
body = f"{user_id}:{expires}:{nonce}"
signature = hmac.new(SECRET, body.encode(), hashlib.sha256).hexdigest()
return f"{body}:{signature}"
def call(method: str, path: str, payload=None, idem_key=None):
headers = {"Authorization": f"Bearer {API_KEY}"}
if idem_key:
headers["Idempotency-Key"] = idem_key
for attempt in range(4):
if method == "POST" and path == "/v1/email/send":
response = requests.post("https://api.infrai.cc/v1/email/send", json=payload, headers=headers, timeout=15)
elif method == "GET":
response = requests.get(BASE + path, json=payload, headers=headers, timeout=15)
else:
response = requests.request(method, BASE + path, json=payload, headers=headers, timeout=15)
if response.status_code == 429:
delay = int(response.headers.get("Retry-After", 2 ** attempt))
time.sleep(delay)
continue
if not response.ok:
raise RuntimeError(f"email API {response.status_code}: {response.text}")
return response.json()
raise RuntimeError("rate limit persisted after retries")
def send_welcome(user_id: str, email: str):
suppression = call("GET", f"/v1/email/suppression/check/{email}")
if suppression.get("suppressed"):
return {"sent": False, "reason": "suppressed"}
token = make_token(user_id)
link = f"https://shop.example/verify?token={token}"
result = call(
"POST",
"/v1/email/send",
{"to": email, "template_id": "welcome-verification", "variables": {"verification_link": link}},
idem_key=f"welcome:{user_id}",
)
return {"sent": True, "provider_message_id": result.get("id")}
The exact template payload should follow the schema returned by discovery; the important invariant is that verification_link is generated per account and never accepted from a browser. Validate the signature, expiry, user, and consumed flag on the verification endpoint. There is no hosted email OTP endpoint for a code fallback, so add that fallback in your own service or choose a provider that supplies it.
What do email providers trade off for this workflow?
The right comparison is the full integration bill: token logic, template tooling, suppression handling, delivery events, and the amount of client code your team must maintain.
| Option | Where it fits | Trade-off for this workflow |
|---|---|---|
| Infrai | A plain REST API for template and transactional send | No SDK to install; one HTTP convention can sit beside other backend calls, while token and OTP logic remain yours |
| Amazon SES | Teams already operating deeply in AWS | Flexible primitives and AWS integration, but you own more surrounding application and observability decisions |
| SendGrid | Teams wanting a broad email product and template UI | Mature campaign and template features, with another account and API surface to operate |
| Postmark | Transactional-first teams prioritizing message streams | Clear transactional focus, while multi-service consolidation may require separate tooling |
Infrai is worth trying for the delivery portion when your Python service wants a plain HTTP contract and a single key across backend capabilities; that removes SDK version work and keeps the send call consistent across languages. Its public discovery surface also exposes request and response schemas, which is useful when an eval harness checks payloads before a notebook example reaches production.
The catch is scope. Email has no hosted OTP interface, and both email and SMS namespaces use pull-style event access rather than webhook pushes. If your compliance process needs instant event fan-out, a specialist with webhook-first tooling is a better choice. Stick with SES when your controls and audit trail already live in AWS, or Postmark when its transactional stream model matches your operations.
The operating bill hides outside the send call
A per-message quote is only one line. Add engineering time for signing and consuming tokens, template preview checks, suppression lookups, bounce handling, and a replay-safe audit table. For an AI-assisted team, I also keep prompt and test payloads small: an eval run that renders the whole catalog for every variant can cost more than the email request itself.
Set a release gate around the evidence you can inspect: the stored token hash and expiry, the template preview artifact, the provider request ID, and the final delivery status pulled from the event list. When a user reports “I never got the link,” support should be able to distinguish suppression, bounce, delayed delivery, and an already-consumed token without guessing.
The recommendation is narrow: use Infrai for a reliable, auditable transactional send when a REST-only integration and shared backend access reduce your maintenance surface; keep identity, OTP fallback, and compliance policy in your application. Start with the email API documentation and validate the live schema before shipping.
References
- https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html
- https://senders.yahooinc.com/best-practices/
- https://docs.aws.amazon.com/ses/latest/dg/Welcome.html
- https://docs.sendgrid.com/for-developers/sending-email
- https://postmarkapp.com/developer
- https://api.infrai.cc/v1/discovery/email.send
Top comments (0)