Send Transactional Emails from Deno Deploy with MailAnvil — Tutorial for AI SaaS Builders
Deno Deploy is the edge runtime of choice for AI SaaS builders. It's fast, TypeScript-native, and deploys in seconds. But every SaaS needs email — and provisioning SMTP on edge is painful.
MailAnvil solves this: REST API, no npm deps, just fetch(). Works anywhere Deno runs.
What you'll build
Three common transactional email patterns:
- Welcome email after signup
- Password reset with magic link
- OTP verification for 2FA
All three in under 50 lines of Deno TypeScript.
Prerequisites
- Deno Deploy account (free tier works)
- MailAnvil API key (get early access at mailanvil.com)
- A verified sending domain
Step 1: Set up your MailAnvil client
No SDK needed. Just fetch() with your API key:
// lib/mailanvil.ts
const MAILANVIL_API = "https://api.mailanvil.com/v1";
const API_KEY = Deno.env.get("MAILANVIL_API_KEY")!;
interface SendEmailParams {
from: string;
to: string[];
subject: string;
html: string;
text?: string;
reply_to?: string;
}
export async function sendEmail(params: SendEmailParams) {
const res = await fetch(`${MAILANVIL_API}/emails`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(params),
});
if (!res.ok) {
const err = await res.json();
throw new Error(`MailAnvil error: ${err.message}`);
}
return res.json();
}
That's it. 15 lines. No npm install, no CJS/ESM drama, no polyfills.
Step 2: Welcome email after signup
// routes/api/signup.ts
import { sendEmail } from "../../lib/mailanvil.ts";
export async function handler(req: Request): Promise<Response> {
const { email, name } = await req.json();
// Save user to DB...
await sendEmail({
from: "[email protected]",
to: [email],
subject: `Welcome to Yourapp, ${name}!`,
html: `
<h1>Welcome, ${name}!</h1>
<p>Thanks for signing up. Here's how to get started:</p>
<ol>
<li>Verify your domain</li>
<li>Send your first email</li>
<li>Check the dashboard</li>
</ol>
<a href="https://yourapp.com/dashboard">Go to dashboard →</a>
`,
});
return new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" },
});
}
Step 3: Password reset with magic link
// routes/api/forgot-password.ts
import { sendEmail } from "../../lib/mailanvil.ts";
import { createResetToken } from "../../lib/auth.ts";
export async function handler(req: Request): Promise<Response> {
const { email } = await req.json();
const token = await createResetToken(email);
const resetUrl = `https://yourapp.com/reset-password?token=${token}`;
await sendEmail({
from: "[email protected]",
to: [email],
subject: "Reset your password",
html: `
<p>You requested a password reset.</p>
<p>
<a href="${resetUrl}">Click here to reset your password</a>
</p>
<p>This link expires in 1 hour.</p>
<p>If you didn't request this, ignore this email.</p>
`,
});
// Always return success (don't leak user existence)
return new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" },
});
}
Step 4: OTP verification email
// routes/api/send-otp.ts
import { sendEmail } from "../../lib/mailanvil.ts";
import { generateOTP, storeOTP } from "../../lib/otp.ts";
export async function handler(req: Request): Promise<Response> {
const { email } = await req.json();
const code = generateOTP();
await storeOTP(email, code);
await sendEmail({
from: "[email protected]",
to: [email],
subject: `Your verification code: ${code}`,
html: `
<div style="text-align: center; padding: 40px;">
<h2>Your verification code</h2>
<div style="font-size: 32px; font-weight: bold;
letter-spacing: 8px; padding: 20px;
background: #f0f0f0; border-radius: 8px;
margin: 20px 0;">
${code}
</div>
<p>This code expires in 5 minutes.</p>
</div>
`,
});
return new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" },
});
}
Step 5: Deploy to Deno Deploy
deployctl deploy --project=yourapp --prod
Set your MAILANVIL_API_KEY in the Deno Deploy dashboard under Settings → Environment Variables.
Why MailAnvil over alternatives on Deno Deploy
| Feature | MailAnvil | Resend | SendGrid |
|---|---|---|---|
| Deno-native (fetch) | ✅ | ✅ | ❌ (SDK only) |
| No npm deps | ✅ | ✅ | ❌ |
| IDR pricing | ✅ | ❌ | ❌ |
| QRIS/GoPay | ✅ | ❌ | ❌ |
| MCP-native | ✅ | ❌ | ❌ |
| Cold start impact | 0ms | 0ms | 200ms+ |
MailAnvil is the only email API where your Deno Deploy function stays fast and your billing stays in Rupiah.
Production checklist
- [ ] Use
reply_toheader so users can reply directly - [ ] Wrap
sendEmailcalls in try-catch (email failures shouldn't crash signup) - [ ] Add retry logic for transient failures (MailAnvil returns 429 with
Retry-After) - [ ] Track delivery via webhooks (set up in MailAnvil dashboard)
What's next
- Webhooks deep-dive — track bounces and opens
- MCP setup guide — let AI agents send email
- Cloudflare Workers tutorial — same patterns on CF Workers
Ready to ship? Get your API key at mailanvil.com. First 10,000 emails free.