Send email with Deno

Explicit permissions, native fetch, no build step.

You need a verified sending domain and an API key from Settings › API Keys. If neither exists yet, the quickstart covers both in a couple of minutes.

Send

const key = Deno.env.get("SENDING_API_KEY")!;
 
Deno.serve(async (req) => {
  const { email, userId } = await req.json();
  const res = await fetch("https://sending.dev/api/v1/emails", {
    method: "POST",
    headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
    body: JSON.stringify({
      from: "Acme <[email protected]>",
      to: email,
      subject: "Welcome",
      html: "<p>You're in.</p>",
      idempotencyKey: `welcome-${userId}`,
    }),
  });
  return Response.json(await res.json(), { status: res.status });
});

A successful call answers 202 with the message id: accepted and queued, not yet delivered. Delivery, opens, bounces and complaints arrive later, on webhooks or in the dashboard.

What changes in Deno

Run it with --allow-net=sending.dev and --allow-env=SENDING_API_KEY: narrow permissions are the point of Deno, and this program needs exactly those two.

The thing that catches people

Without --allow-env the key reads as undefined and the API answers 401. The permission error and the auth error look nothing alike, so check permissions first.

Next

  • All the fields: cc and bcc, reply-to, attachments, templates, scheduling.
  • Webhooks: delivery, bounce and complaint events, signed with HMAC.
  • Domains: SPF, DKIM and MAIL FROM, and why sending is refused until they are in place.
  • MCP: the same operations as tools, when the one writing the code is an agent.

Nearby: JavaScript and TypeScript

All 30 stacks