How to Send Emails in Deno (2026 Guide)

Nik@nikpolale
10 min read
Deno has fetch built in. No need for node-fetch or axios. Email sending is one HTTP call to an API provider. Deno's permissions model keeps your API keys secure by requiring explicit --allow-net and --allow-env flags.
This guide covers email sending with Deno's native APIs and common frameworks.
Create an Email Client
email.ts
const SEQUENZY_API_KEY = Deno.env.get("SEQUENZY_API_KEY")!;
export async function sendEmail(to: string, subject: string, body: string) {
const response = await fetch("https://api.sequenzy.com/v1/transactional/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${SEQUENZY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ to, subject, body }),
});
if (!response.ok) {
throw new Error(`Email failed: ${response.statusText}`);
}
return response.json();
}email.ts
const RESEND_API_KEY = Deno.env.get("RESEND_API_KEY")!;
export async function sendEmail(to: string, subject: string, html: string) {
const response = await fetch("https://api.resend.com/emails", {
method: "POST",
headers: {
"Authorization": `Bearer ${RESEND_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: "Your App <noreply@yourdomain.com>",
to, subject, html,
}),
});
if (!response.ok) {
throw new Error(`Email failed: ${response.statusText}`);
}
return response.json();
}email.ts
const SENDGRID_API_KEY = Deno.env.get("SENDGRID_API_KEY")!;
export async function sendEmail(to: string, subject: string, html: string) {
const response = await fetch("https://api.sendgrid.com/v3/mail/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${SENDGRID_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
personalizations: [{ to: [{ email: to }] }],
from: { email: "noreply@yourdomain.com" },
subject,
content: [{ type: "text/html", value: html }],
}),
});
if (!response.ok) {
throw new Error(`Email failed: ${response.statusText}`);
}
}Send with Deno.serve
// main.ts
import { sendEmail } from "./email.ts";
Deno.serve({ port: 3000 }, async (req) => {
if (req.method === "POST" && new URL(req.url).pathname === "/api/send-welcome") {
const { email, name } = await req.json();
if (!email || !name) {
return Response.json({ error: "email and name required" }, { status: 400 });
}
try {
const result = await sendEmail(
email,
`Welcome, ${name}`,
`<h1>Welcome, ${name}</h1><p>Your account is ready.</p>`,
);
return Response.json(result);
} catch {
return Response.json({ error: "Failed to send" }, { status: 500 });
}
}
return new Response("Not found", { status: 404 });
});Run with:
deno run --allow-net --allow-env main.tsSend with Fresh
// routes/api/send-welcome.ts
import type { Handlers } from "$fresh/server.ts";
import { sendEmail } from "../../email.ts";
export const handler: Handlers = {
async POST(req) {
const { email, name } = await req.json();
if (!email || !name) {
return Response.json({ error: "email and name required" }, { status: 400 });
}
try {
const result = await sendEmail(
email,
`Welcome, ${name}`,
`<h1>Welcome, ${name}</h1><p>Your account is ready.</p>`,
);
return Response.json(result);
} catch {
return Response.json({ error: "Failed to send" }, { status: 500 });
}
},
};Going to Production
1. Verify Your Domain
Add SPF, DKIM, DMARC DNS records.
2. Use Deno Deploy
Deno Deploy runs your code at the edge. Environment variables are set in the dashboard.
3. Permissions
Only grant what you need:
deno run --allow-net=api.sequenzy.com --allow-env=SEQUENZY_API_KEY main.tsBeyond Transactional
Sequenzy handles transactional sends, marketing campaigns, automated sequences, and subscriber management from one API. Native Stripe integration for SaaS.
Wrapping Up
- Built-in fetch for zero-dependency email sending
- Deno.serve for lightweight HTTP servers
- Fresh handlers for full-stack Deno apps
- Permission flags for secure API key access
Pick your provider, copy the patterns, and start sending.