Transactional Email for AI Apps (2026): Codes, Invites, and Login Links
The four auth emails most AI apps need—OTP, verify links, magic login, and invites—and how to send each with one POST /emails call on Rovela.
What an AI app actually needs to email
If you are wiring an AI product together—chat UI, agents, a thin backend—you will hit the same four emails before you ship anything else:
- OTP / verification codes — six digits at sign-up or login.
- Email verification links — click to confirm the address (not a typed code).
- Magic login links — passwordless sign-in for a returning user.
- Team invites — “join this workspace” with an accept URL.
These are transactional, not marketing. They must arrive fast, look trustworthy, and never invent a second API surface for each flow. Rovela’s job is delivery: DKIM on your domain, retries, bounce handling, and a message id you can look up later. Your app’s job is everything security-sensitive: generate the code or token, store a hash, set expiry, and redeem it once.
The official recipes for codes and reset-style links live in the docs:
Magic login, invite, and verify-link flows are application patterns built on the same send call—not separate Rovela product features.
One API call: POST /emails
Every auth email above is the same HTTP request. Auth with a sending_access key (Send Email):
curl -X POST https://api.rovela.dev/emails \
-H "Authorization: Bearer re_your_sending_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: auth-alice-8f3a2c71" \
-d '{
"from": "Acme <security@mail.acme.com>",
"to": "alice@example.com",
"subject": "Your Acme sign-in code: 482913",
"html": "<p>Your code is <b>482913</b>. It expires in 10 minutes.</p>",
"text": "Your code is 482913. It expires in 10 minutes."
}'
A 200 with {"object":"email","id":"…"} means the message was queued, not that it already landed in the inbox. Send both html and text for auth mail—clients often surface the text part in notification previews, which is where people read a code or URL.
Use a verified subdomain for from (for example mail.yourdomain.com), keep security traffic off your root domain, and set a fresh Idempotency-Key per logical send so a timeout retry does not deliver two disagreeing codes.
OTP codes: when to use them
Use a short numeric code when the user is already on a device that can type: mobile sign-up, desktop login challenge, “confirm this is you.” Generate with a CSPRNG, store only a keyed hash (HMAC with a server pepper), expire in minutes, and bound attempts. Rovela delivers the message; it does not issue or validate the digits.
The full Node/Python walkthrough—schema, constant-time compare, resend limits—is in Email verification codes. For a product-oriented overview of the same send path, see Send OTP via email API.
Keep the subject human and specific (Your Acme code: 482913). Put the code in both bodies. Do not rely on click tracking for OTPs—there is nothing to click.
Verify links vs codes
A verification link proves the mailbox without asking the user to copy six digits. Your app mints a high-entropy token, stores a hash + expiry, and emails an absolute URL such as https://app.acme.com/verify?token=…. On GET, compare hashes, mark the account verified, and invalidate the token.
Prefer links when the user is on a phone and switching apps is painful, or when you want one-tap confirm after OAuth/email capture. Prefer codes when the user must stay on the same screen (embedded webviews, kiosks, or flows where opening mail in another app would lose context).
The token lifecycle mirrors the password reset example: hash at rest, short TTL, single use. Deep dive: Email verification links.
Always put the full URL in the plain-text part. If you enable click tracking elsewhere, turn it off for verify links (or send them from a dedicated subdomain) so the destination is not rewritten.
Magic login links
Magic login is the same link pattern aimed at session creation instead of “mark email verified.” User requests sign-in → you store a one-time token → email the URL → redeem creates a session and burns the token.
Do not invent a Rovela “magic login API.” There isn’t one. You own the token table; Rovela owns delivery via POST /emails. Subject lines should say what will happen (“Sign in to Acme”) and never look like a marketing blast. Full walkthrough: Magic login email links.
Rate-limit requests per address and per IP. Treat a redeemed or expired token as a soft failure with a generic message so attackers cannot probe validity.
Team invites
Invites are link emails with role metadata: workspace id, invited email, role, expiry. Generate a token, email https://app.acme.com/invites/accept?token=…, and on accept attach the user to the team. If they do not have an account yet, create one or park them in sign-up with the invite bound.
Again: Rovela’s console “invitation” for dashboard teammates is unrelated. Your product invite is just another transactional send. Details and pitfalls (wrong inbox, role escalation, revoked invites): Team invite email links.
Send invites from a recognizable from on your mail subdomain. Include who invited them and which workspace in both HTML and text.
Shared rules that keep auth mail safe
These apply to every flow above:
| Rule | Why |
|---|---|
html and text | Previews and clients that strip HTML still show the code/URL |
Idempotency-Key | Retries must not mint a second live secret |
| Verified subdomain | Isolates auth reputation from newsletters |
| Tracking off for link mail | Destination must stay exactly your URL |
Treat 200 as queued | Confirm delivery with webhooks or the dashboard if you need certainty |
| Respect suppression | Addresses on the suppression list return 400—fix before resending |
Start from pricing only for plan context; do not hard-code numbers that change. Use the smallest key scope that can send (sending_access) on the machines that emit OTPs and links.
Next steps
- Verify a sending domain and create a
sending_accesskey. - Ship OTP with the official verification-code example.
- Add link flows using the password-reset pattern for verify, magic login, and invites.
- Read the sibling posts when you implement each one:
One POST /emails covers the auth surface most AI apps need. Keep secrets in your database; keep delivery on Rovela.