Nodemailer wants config. Fetch wants a URL.
Nodemailer is the default answer, and it front-loads the work: a transporter, an SMTP host, a port, auth credentials, and a connection to keep healthy. All of that before the first send, and none of it helps the email land.
The API path is the fetch call Node already ships with, or the SDK when you want types and defaults.
One POST, and the send is handled
import { Nitrosend } from "@nitrosend/sdk" const ns = new Nitrosend(process.env.NITROSEND_API_KEY) await ns.messages.send({ from: "[email protected]", to: "[email protected]", subject: "Welcome", html: "<p>You're in.</p>", })
npm install @nitrosend/sdk, one env var, one call. Prefer zero dependencies? The same send is a plain fetch POST to /v1/my/messages. Either way you get a message ID back before the response settles into your logs.
And the agent path is one line too: claude mcp add nitrosend -- npx -y @nitrosend/mcp. Both packages live under the nitrosend org on npm, @nitrosend/sdk and @nitrosend/mcp, alongside a full OpenAPI spec at api.nitrosend.com/openapi.yaml that we keep validated on every API change. The npm ecosystem your project lives in is the ecosystem we ship in.
The SDK is built the way Node people want it built: fully typed, zero dependencies, Node 18 and up, with typed errors you catch with instanceof rather than string-matching a message. And if you're wondering who else is on the other end of these calls: about half our client traffic is Claude in one form or another, with ChatGPT, Cursor, curl, n8n, and plain Node filling out the rest. Node is not an afterthought SDK for us. It's the front door.
Full parameters, responses, and error codes live in the REST API docs and the API reference.
And the part no library gives you
The same account exposes an MCP server, so the agent writing your code can also run your email. Point Claude, Cursor, or Codex at api.nitrosend.com/mcp and it composes, previews, and stages sends, with a human approving every one. The API is for your code. The MCP is for your agent. One platform either way.
Every send comes back with a message ID, and delivery events follow on your webhook: delivered, opened, bounced. No dashboard required to know what happened.