npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

send16-mail

v0.9.0

Published

Node.js SDK for the Send16 email platform. Send transactional + marketing email from any stack. Get started: npx create-send16

Readme

send16-mail — Email for developers

The official Node.js SDK for Send16 — transactional and marketing email that just works.

Zero runtime dependencies. ~22 KB on disk, no transitive packages, no supply-chain surface. Works in Node.js 18+, Bun, Deno, Cloudflare Workers, and any other runtime that ships fetch. TypeScript-first.

Fastest start

npx create-send16

Detects your framework (Next.js / Astro / Remix / SvelteKit / Nuxt / Hono / Express / Vite + React / plain Node), opens your browser to authorize, writes SEND16_API_KEY to .env.local, and drops an idiomatic lib/send16 module plus a working example route. ~60 seconds to first send, no pasted API keys.

Manual install

npm install send16-mail
  1. Create a free account — 1,000 emails/month free.
  2. Open the dashboard → Developers → API KeysCreate key.
  3. Set SEND16_API_KEY in your environment.

Quick start

import { Send16 } from 'send16-mail';

// Reads SEND16_API_KEY from process.env automatically.
// Or pass explicitly: new Send16('sk_live_xxxxx')
const send16 = new Send16();

const { data, error } = await send16.emails.send({
  from: 'Acme <[email protected]>',
  to: ['[email protected]'],
  subject: 'Hello from Send16',
  html: '<p>Hello world</p>',
});

if (error) console.error('Failed:', error.message);
else console.log('Sent:', data.id);

Sending to multiple people

to, cc, and bcc each accept either a single email or an array of up to 50 addresses. One send() call → one log row → one email thread that lands in every recipient's inbox. Do not loop and call send() once per recipient — that doubles your API usage and breaks the thread.

// All peers — every recipient sees every other recipient in the To: header
await send16.emails.send({
  from: 'Acme Ops <[email protected]>',
  to: ['[email protected]', '[email protected]'],
  subject: 'Production alert',
  html: '<p>...</p>',
});

to[] vs cc — when to use which. Multiple to recipients are peers (everyone is a primary recipient and sees each other). For a "primary recipient with a copy to someone else" pattern — form-submission notifications, audit mailboxes, account-manager visibility — use to for the primary and cc for the copies. That's what your recipients expect semantically.

// Form notification: firm owns it, internal address gets a copy
await send16.emails.send({
  from: 'Contact Form <[email protected]>',
  to: '[email protected]',
  cc: ['[email protected]'],
  subject: 'New enquiry from Jane Doe',
  html: '<p>...</p>',
});

Use bcc when the copy should be silent — neither the to nor cc recipients see it. Useful for compliance archives or oncall mirrors.

Attachments, CC/BCC, custom headers

import { readFileSync } from 'node:fs';

await send16.emails.send({
  from: 'Acme Billing <[email protected]>',
  to: '[email protected]',
  cc: ['[email protected]'],
  bcc: '[email protected]',
  subject: 'Your invoice #1234',
  html: '<p>Invoice attached.</p>',
  attachments: [
    {
      filename: 'invoice-1234.pdf',
      content: readFileSync('./invoice-1234.pdf').toString('base64'),
      contentType: 'application/pdf',
    },
  ],
  headers: {
    'X-Customer-Id': 'cust_8f2a',
    'X-Invoice-Id': 'inv_1234',
  },
});

Limits. Up to 10 attachments per send, 10 MB total decoded (1 MB when scheduledAt is set, to keep the queue row light). Up to 20 custom headers, names match [A-Za-z0-9-]+, no CR/LF in values. Headers Send16 owns (from, to, subject, cc, bcc, reply-to, message-id, date, mime-version, content-*) cannot be overridden — use the dedicated fields instead.

Tasteful HTML in 5 lines — compose()

Don't want to write MJML or React Email? Pass a plain JS object and compose() returns a responsive, mobile-friendly HTML body plus a plain-text fallback. Pure function, runs anywhere, no API call needed for the render itself.

Welcome email

import { compose } from 'send16-mail';

const { html, text } = compose({
  product: { name: 'Acme', link: 'https://acme.com' },
  body: {
    name: 'John',
    intro: 'Welcome to Acme — your account is ready.',
    action: {
      instructions: 'Confirm your email to get started:',
      button: { text: 'Confirm email', link: 'https://acme.com/verify?t=abc' },
    },
    outro: 'Need help? Just reply to this email.',
  },
});

Password reset

const { html, text } = compose({
  product: { name: 'Acme' },
  body: {
    name: 'John',
    intro: 'You requested a password reset for your Acme account.',
    action: {
      instructions: 'Click the button below to choose a new password:',
      button: {
        text: 'Reset password',
        link: 'https://acme.com/reset?t=abc',
        color: '#dc2626',
      },
    },
    outro: 'If you did not request this, you can safely ignore the email.',
  },
});

Receipt

const { html, text } = compose({
  product: { name: 'Acme' },
  body: {
    name: 'John',
    intro: 'Thanks for your purchase! Here is your receipt.',
    table: {
      data: [
        { Item: 'Pro plan',      Quantity: '1', Price: '$29.00' },
        { Item: 'Extra seat',    Quantity: '2', Price: '$10.00' },
        { Item: 'Total',         Quantity: '',  Price: '$39.00' },
      ],
      columns: { customAlignment: { Price: 'right' } },
    },
    outro: 'Questions about your invoice? Reply and we will help.',
  },
});

Compose + send in one call

await send16.compose.send({
  from: 'Acme <[email protected]>',
  to: '[email protected]',
  subject: 'Welcome!',
  product: { name: 'Acme' },
  body: { name: 'John', intro: 'Thanks for signing up.' },
});

Coming from mailgen?

send16-mail's compose() takes the same { product, body } shape — drop in your existing payload and you'll get HTML out the other side. The difference: we also send the email through a hosted, deliverability-tuned pipeline (DKIM/SPF/DMARC, dedicated IPs, bounce handling, opens/clicks, automations) instead of leaving you to wire up nodemailer + SES yourself.

// Roughly: replace this
const html = mailGenerator.generate({ body: { name, intro, action } });
await transporter.sendMail({ from, to, subject, html });

// …with this
await send16.compose.send({ from, to, subject, product, body: { name, intro, action } });

Why send16-mail

  • Zero dependencies — 22 KB SDK, no transitives, runs anywhere fetch exists
  • Edge-ready — Cloudflare Workers, Vercel Edge, Deno Deploy out of the box
  • Resend-compatible API — easy migration from Resend / SendGrid / Postmark / Mailgun
  • Self-hosted option — bring your own server if you want full control
  • Transactional + marketing — one platform, one SDK
  • Free tier — 1,000 emails/month, no credit card

Configuration

const send16 = new Send16('sk_live_your_key', {
  baseUrl: 'https://api.send16.com', // default; override for self-hosted
  timeout: 30_000,                    // ms; default 30s
});

Emails

Send

const { data, error } = await send16.emails.send({
  from: 'Acme <[email protected]>',
  to: ['[email protected]'],
  subject: 'Hello',
  html: '<p>Hello world</p>',
});

All options

await send16.emails.send({
  from: 'Acme <[email protected]>',
  to: ['[email protected]'],
  cc: ['[email protected]'],
  bcc: ['[email protected]'],
  replyTo: '[email protected]',
  subject: 'Invoice #1234',
  html: '<p>Your invoice is attached.</p>',
  text: 'Your invoice is attached.',
  headers: { 'X-Custom-Header': 'value' },
  attachments: [
    { filename: 'invoice.pdf', content: '<base64>', contentType: 'application/pdf' },
  ],
  tags: [{ name: 'category', value: 'invoices' }],
  scheduledAt: '2026-04-01T09:00:00Z',
});

Batch (up to 100)

await send16.emails.batch([
  { from: 'Acme <[email protected]>', to: ['[email protected]'], subject: 'Hi A', html: '<p>Hi!</p>' },
  { from: 'Acme <[email protected]>', to: ['[email protected]'], subject: 'Hi B', html: '<p>Hi!</p>' },
]);

Contacts

await send16.contacts.create({ email: '[email protected]', firstName: 'John' });
await send16.contacts.list({ page: 1, limit: 50 });
await send16.contacts.update('id', { firstName: 'Jane' });
await send16.contacts.delete('id');

Domains

await send16.domains.list();
await send16.domains.verify('domain-id');

Render (compile dashboard email content → HTML)

const { data } = await send16.render.run({
  content: emailContent, // EmailContent JSON from the Send16 editor
  variables: { firstName: 'John' },
});
console.log(data.html); // exact HTML the API will send

Events (trigger automations)

await send16.events.send({
  name: 'signup_completed',
  contact: '[email protected]',
  payload: { plan: 'pro' },
});

Error handling

The SDK never throws. Every method returns { data, error }:

const { data, error } = await send16.emails.send({ /* ... */ });
if (error) {
  console.error(error.code);    // "HTTP_422" | "TIMEOUT" | "NETWORK_ERROR" | ...
  console.error(error.message);
  return;
}
console.log(data.id); // non-null when error is null

TypeScript

Full types are bundled. Common imports:

import type {
  SendEmailPayload,
  SendEmailResponse,
  Contact,
  Domain,
  ComposeInput,
  ComposeResult,
} from 'send16-mail';

Companion packages

  • send16-clisend16 command-line tool: send local .tsx/.mjml/.html files, manage domains/contacts, fire events.
  • send16-editor — embeddable React block editor your users can compose emails in directly inside your app.

Links

License

MIT