How to send email
from a Node.js AI agent.
The shortest working recipe for making a Node.js agent send email from its own DKIM-signed address. No SMTP, no OAuth.
For Node.js agents, sending email is normally either 'set up SMTP with nodemailer' or 'install the Resend/SendGrid SDK.' Both give you sending credentials without an inbox and without per-agent identity. Loomal gives you both in one HTTP call.
This recipe uses the built-in fetch (Node 18+). If you're on older Node, swap in undici or node-fetch; the shape is identical.
1. Get an API key
Sign up at console.loomal.ai, create an identity, and copy the API key (loid-...). Your agent now has a DKIM-signed address it can send from.
LOOMAL_API_KEY=loid-your-api-key2. Send with fetch
POST to /v0/messages/send. The body is a JSON object with to (array), subject (string), and text (string). Response includes messageId and threadId — save the threadId if you plan to correlate replies later.
const API = "https://api.loomal.ai/v0";
async function send(to: string, subject: string, text: string) {
const res = await fetch(`${API}/messages/send`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.LOOMAL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ to: [to], subject, text }),
});
if (!res.ok) throw new Error(`Loomal ${res.status}: ${await res.text()}`);
return res.json() as Promise<{ messageId: string; threadId: string }>;
}
await send("alice@example.com", "Hello", "From a Node.js agent.");3. Wrap as a tool for the Vercel AI SDK
If your agent uses the AI SDK's tool-calling loop, define the tool with zod and hand it to streamText. The model decides when to call it.
import { tool, streamText } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
const sendEmail = tool({
description: "Send an email from the agent's own address.",
parameters: z.object({
to: z.string().email(),
subject: z.string(),
text: z.string(),
}),
execute: async ({ to, subject, text }) => send(to, subject, text),
});
const result = await streamText({
model: openai("gpt-4o-mini"),
tools: { sendEmail },
maxSteps: 4,
prompt: "Email alice@example.com thanking her for today's demo.",
});4. Or use the MCP server
For a shorter path, register Loomal as an MCP server. The AI SDK, LangChain, Mastra, and Claude Agent SDK all support this; the agent gets mail.send, mail.reply, and the rest without any tool wrapper code. See the Vercel AI SDK guide for the full setup.
import { experimental_createMCPClient } from "ai";
import { Experimental_StdioMCPTransport } from "ai/mcp-stdio";
const client = await experimental_createMCPClient({
transport: new Experimental_StdioMCPTransport({
command: "npx",
args: ["-y", "@loomal/mcp"],
env: { LOOMAL_API_KEY: process.env.LOOMAL_API_KEY! },
}),
});
const tools = await client.tools();FAQ
Does this work in edge runtimes (Vercel Edge, Cloudflare Workers)?
The REST approach does — fetch is available. The MCP stdio transport is not, since edge runtimes can't spawn subprocesses. Use the HTTP MCP transport or just call REST directly from edge.
Can I send HTML instead of text?
Yes — add an html field to the body alongside text. Most email clients prefer the HTML version but fall back to text when it's missing.
How do I send to multiple recipients?
The to field is an array. You can also include cc and bcc arrays. Each message is delivered once per recipient; Loomal handles the envelope addressing.
Related reading
Last updated: 2026-04-15