YourMail

Framework guide

Send email from Node.js and Express

Why use this

The client is a zero-dependency TypeScript package that works in any Node process — a server, a worker, a one-off script. This guide covers the plain call and then a real endpoint around it.

For example

Someone signs up through your Express API and gets a welcome email before the response returns.

1. Install

npm install yourmail

2. The shortest working send

Supplying both html and text is worth the extra line: some clients prefer the plain-text part, and its absence is a mild spam signal.

import { YourMail } from "yourmail";

const yourmail = new YourMail(process.env.YOURMAIL_API_KEY);

const { id } = await yourmail.send({
  from: "Acme <hello@mail.acme.com>",
  to: "customer@example.com",
  subject: "Welcome aboard",
  html: "<p>Thanks for signing up.</p>",
  text: "Thanks for signing up.",
});

console.log("queued", id);

3. An Express endpoint

import express from "express";
import { YourMail, ValidationError, RateLimitError } from "yourmail";

const app = express();
app.use(express.json());

const yourmail = new YourMail(process.env.YOURMAIL_API_KEY);

app.post("/signup", async (req, res, next) => {
  const { email } = req.body;

  try {
    const { id } = await yourmail.send({
      from: "Acme <hello@mail.acme.com>",
      to: email,
      subject: "Welcome aboard",
      html: "<p>Thanks for signing up.</p>",
      // Makes a retried signup safe: the same key returns the original id
      // rather than mailing the customer twice.
      idempotencyKey: `welcome:${email}`,
      tags: [{ name: "type", value: "welcome" }],
    });
    res.json({ id });
  } catch (err) {
    if (err instanceof ValidationError) {
      // Bad address, or a recipient on your suppression list. Retrying this
      // unchanged will fail identically — tell the caller instead.
      return res.status(422).json({ error: err.message });
    }
    if (err instanceof RateLimitError) {
      return res.status(429).json({ error: "Try again shortly." });
    }
    next(err);
  }
});

app.listen(3000);

tags are optional labels that show up when you filter your message history later — useful once you are sending more than one kind of email.

4. Don't write a retry loop

This is the single most common way to make a working integration worse. The SDK already retries transient failures with backoff; wrapping it in your own loop retries the permanent failures too, which cannot succeed and merely burns your rate limit.

// Don't do this.
for (let attempt = 0; attempt < 5; attempt++) {
  try {
    await yourmail.send(options);
    break;
  } catch {
    // A ValidationError will fail all five times, and a RateLimitError will
    // fail harder the more you hammer it.
  }
}

The rule is the error type, not the attempt count: a ValidationError is permanent and should never be retried, while a RateLimitError or ServerError is transient. The full list is in the errors reference.

Sending a lot at once

Looping over send() for a few hundred recipients works but is slow and wasteful. Use the batch endpoint instead — up to 100 messages per request, each with its own recipients and body.