Send Email from an AI Coding Agent (2026) via HTTP
Give Cursor or Claude a sending_access key and POST /emails for OTP, invites, and magic links—no SMTP daemon, with idempotency and verified domains.
Why an AI coding agent needs email
When you ask Cursor or Claude to scaffold a product, the first “real” feature is rarely the UI. It is an account flow: a six-digit code, a magic login link, a team invite, or a “confirm this address” URL. Those messages are transactional email—they must leave a verified domain, survive retries, and not double-send when the agent’s tool call times out.
Rovela positions itself as email infrastructure for developers and AI agents: a plain HTTP API, DKIM-signed delivery, inbound webhooks, and the same send path whether a human typed curl or an agent ran a tool. There is no separate “agent protocol” to learn. If the runtime can call HTTPS with a bearer token from the environment, it can send mail.
That framing matters for SEO and for safety. You do not need an official Cursor plugin, Claude tool, or MCP server to ship OTP mail. You need the Quick Start, a sending_access key, and the habit of never pasting secrets into the chat transcript.
The one request agents actually make
Everything reduces to one authenticated call:
curl -X POST https://api.rovela.dev/emails \
-H "Authorization: Bearer $ROVELA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: signup-otp-alice-20261104" \
-d '{
"from": "Acme <security@mail.yourdomain.com>",
"to": "alice@example.com",
"subject": "Your Acme code: 482913",
"html": "<p>Your verification code is <b>482913</b>.</p><p>It expires in 10 minutes.</p>",
"text": "Your verification code is 482913. It expires in 10 minutes."
}'
A successful response looks like {"object":"email","id":"…"}. Per the Send Email API, that 200 means the message was accepted for delivery, not that it already landed in the inbox. Delivery is asynchronous; use message events or webhooks when you need proof.
The agent (or the app it wrote) should treat this the same way a senior engineer would: put the key in env, keep token generation in your database, and send both html and text for security-sensitive mail so notification previews still show the code or the full URL.
Environment, least privilege, verified domain
Follow the authentication rules the dashboard already teaches:
- Verify a sending domain in the dashboard and wait for DNS. Sends from an unverified domain are refused with
400—they are not queued and silently dropped (Quick Start). - Create an API key that looks like
re_…. Prefersending_access: it can queue mail and nothing else. Reservefull_accessfor admin tooling that reads events or manages domains. - Export the key only into the server or sandbox environment the agent is allowed to use (
ROVELA_API_KEY), never into a browser bundle, a committed.envin a public repo, or a prompt that will be logged. - Optionally restrict a key to a single domain if you send on behalf of many customers.
Homepage marketing lists Claude and Cursor among environments that “work with” Rovela because they can issue HTTP. That is the whole integration story: the agent writes or runs the same fetch / curl a human would. Do not invent a product surface that is not in the docs.
Security checklist for agent sessions:
- Tell the agent: “read the key from
$ROVELA_API_KEY; do not print it; do not echo it into commits or issue comments.” - Rotate with create-then-revoke if a transcript or CI log ever captured a key (Authentication).
- Keep OTP hashes, magic-link tokens, and invite secrets in your store—Rovela delivers the message; it does not mint login tokens for your app.
Plans and quotas live on Pricing. This article does not invent numbers; check that page when you size a free plan versus paid volume.
OTP, invites, and magic links—brief patterns
The HTTP body is boring on purpose. What changes is the application half:
| Flow | What you build | What you send |
|---|---|---|
| Email OTP | Random code, store HMAC, expiry, attempt limits | Code in subject + html/text (verification code example) |
| Magic login | Single-use opaque token, redeem once | Absolute HTTPS URL in html and text (magic login links) |
| Team invite | Invite row + accept route | Invite URL with role context (team invite links) |
| Verify address | Confirm token, flip email_verified | Confirmation URL (email verification links) |
For link mail, put the full URL in the plain-text part. If you enable click tracking elsewhere, keep it off for security links so the href the user sees is the one your app issued. Deeper recipes live in the posts above and in the AI apps transactional overview.
A minimal Node-shaped send the agent might emit:
const res = await fetch("https://api.rovela.dev/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ROVELA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify({
from: "Acme <security@mail.yourdomain.com>",
to: email,
subject: `Your Acme code: ${code}`,
html: `<p>Your code is <b>${code}</b>. It expires in 10 minutes.</p>`,
text: `Your code is ${code}. It expires in 10 minutes.`,
}),
});
Same shape works from Python requests, Go net/http, or a shell tool the agent invokes.
Idempotency when agents retry
Agents retry. So do serverless cold starts and flaky Wi‑Fi. Send an Idempotency-Key (up to 256 characters). A replay of the same request within 24 hours returns the original response instead of delivering a second, conflicting OTP.
Practical conventions:
- Derive the key from a stable business id:
otp:{userId}:{purpose}:{window}or a UUID created once before the first attempt and reused on retries. - Do not generate a new key on every retry loop—that defeats the feature.
- Treat
429/5xxas retryable; treat400(bad domain, suppressed recipient) as a configuration or address problem, not a “send again harder” problem.
What not to automate
Giving an agent a sending key is powerful; scope the blast radius:
- Do not let the agent create
full_accesskeys or change DNS unless a human is reviewing. - Do not paste live keys into the chat so the model can “remember” them—use the host env or a secrets manager the sandbox already mounts.
- Do not ask the agent to invent SMTP daemons, open relays, or “just disable DKIM.” Rovela refuses unsigned/unverified sends by design.
- Do not treat inbox placement as solved because the API returned
200. Auth mail that lands in spam needs domain hygiene and copy discipline (OTP and magic-link spam guide). - Do not bulk-blast marketing from the same subdomain you use for codes; keep reputation lanes separate.
Inbound mail and support@ style loops are a different surface—see the inbound email API / webhooks post when the agent needs to receive as well as send.
Ship the next piece
- Walk Quick Start: account → domain →
sending_accesskey → firstPOST /emails. - Wire one auth flow with API: Send Email and Authentication.
- Steal patterns from transactional email for AI apps, then deepen with magic login, invites, or verification links.
- Compare product shape (API + mailboxes + inbound) on Pricing and, if you are migrating, the Resend alternative write-up.
An AI coding agent is fastest when the email layer is boring HTTP. Give it a least-privilege key in the environment, a verified domain, and an idempotent POST /emails—and keep the secrets out of the transcript.