Skip to main content

Node.js guide

How to send email with Node.js

Send password resets, receipts and notifications from your Node.js or TypeScript app with one function call. Free for 3,000 emails a month.

Quickstart

Send your first email in three steps.

You need a domain you control and Node.js 20 or later. Setup takes a few minutes, most of it waiting for DNS.

  1. Step 1: Verify your sending domain

    Add your domain in the Sendaro dashboard and publish the three DNS records it shows you: DKIM, SPF and DMARC. These prove to Gmail, Outlook and other providers that you are allowed to send as that domain, which keeps your email out of spam. Sendaro checks the records and marks the domain verified.

    Domain setup in the docs

  2. Step 2: Install the SDK and add your API key

    Create an API key with send access, then install the official Node.js and TypeScript SDK. Put the key in your environment, never in client code.

    Terminal
    npm install sendaro
    .env
    SENDARO_API_KEY=rf_your_api_key
  3. Step 3: Send the email

    Call send() with a verified from address. Include a text version next to your HTML: some people read plain-text email, and spam filters like seeing both.

    send.ts
    import { Sendaro } from "sendaro";
    
    const sendaro = new Sendaro(); // reads SENDARO_API_KEY
    
    const { id } = await sendaro.send({
      from: "Acme <hello@yourdomain.com>",
      to: "maya@example.com",
      subject: "Reset your password",
      html: "<p>Use this link to choose a new password.</p>",
      text: "Use this link to choose a new password.",
    });
    
    console.log("Queued email", id);

Without the SDK

Send email from Node.js with fetch.

Prefer no dependencies? The SDK is a thin wrapper over one REST call. Any HTTP client works, and so does any other language.

send.mjs
// Node.js 18+ has fetch built in. No package needed.
const response = await fetch("https://sendaro.email/api/emails", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SENDARO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "Acme <hello@yourdomain.com>",
    to: "maya@example.com",
    subject: "Your receipt",
    html: "<p>Thanks for your order.</p>",
  }),
});

if (!response.ok) throw new Error(`Send failed: ${response.status}`);
const { id } = await response.json(); // 202 Accepted

Production

Retries, errors and delivery status.

Networks fail and jobs run twice. These are the three things to get right before you ship.

Retry without sending twice

Pass an idempotencyKey built from something stable, like a user ID and a token. If your job retries with the same key and body within 24 hours, Sendaro returns the original email instead of sending a duplicate. A non-2xx response throws a SendaroError.

confirm-email.ts
import { Sendaro, SendaroError } from "sendaro";

const sendaro = new Sendaro();

try {
  await sendaro.send(
    {
      from: "Acme <hello@yourdomain.com>",
      to: user.email,
      subject: "Confirm your email",
      html: `<p><a href="${confirmUrl}">Confirm your email</a></p>`,
    },
    // Same key, same body within 24 hours: you get the first email back, not a second send.
    { idempotencyKey: `confirm-${user.id}-${token}` },
  );
} catch (error) {
  if (error instanceof SendaroError) {
    // error.status is the HTTP status, error.code is the API error string.
    console.error(error.status, error.code);
  }
  throw error;
}

Check what happened

Accepted is not delivered. Every email has a status that moves from queued to sent, and only becomes delivered when the recipient's mail server confirms it. Bounces and complaints go on your suppression list automatically, so you never keep mailing an address that can't receive.

status.ts
const email = await sendaro.get(id);

console.log(email.status); // "queued" | "sent" | "delivered" | "bounced" | ...

Over 60 requests a minute per key you get a 429 with a Retry-After header. Wait, then retry with the same idempotency key. See rate limits.

API or SMTP

Why use an email API instead of Nodemailer and SMTP?

Nodemailer is a good library, but it still needs an SMTP server, credentials and a port your host doesn't block (many cloud hosts block SMTP ports). Once a message leaves, you have no record of whether it arrived.

With an email API you send one HTTPS request. You get an ID back for every message, a status you can query, signed webhooks for each event, and a dashboard that shows your bounce and complaint rates. That is what tells you a password reset never reached a customer before they tell you.

FAQ

Node.js email questions.

Node.js 20 or later. The package works with both import and require, and ships its own TypeScript types.

No. Sendaro is an HTTP API, so there is no SMTP host, port or password to configure. You send JSON over HTTPS and get an email ID back that you can look up later.

No. Your API key can send email as your domain, so keep it on the server. Call Sendaro from your backend, a serverless function or a Next.js route handler.

A 202 means the email was accepted and queued, not delivered. Read its status with sendaro.get(id), or set up a signed webhook to get email.delivered and email.bounced events as they happen.

The Free plan includes 3,000 emails a month and needs no card. Paid plans add volume; every plan has the full API.

Send your first email today.

Free includes 3,000 emails every month. No card needed.