Send16 API Reference

Send transactional emails with a simple API. Use our Node.js SDK or call the REST API directly from any language.

Base URL:https://api.send16.com

Getting Started

Install the SDK and send your first email in under a minute.

1. Install

npm install send16-mail

2. Send your first email

import { Send16 } from 'send16-mail';

const send16 = new Send16('sk_live_xxxxx');

const { data, error } = await send16.emails.send({
  from: 'Acme <hello@acme.com>',
  to: 'user@gmail.com',
  subject: 'Hello from Send16',
  html: '<h1>Welcome!</h1><p>You\'re all set.</p>',
});

if (error) {
  console.error(error.message);
} else {
  console.log('Sent:', data.message_id);
}
Get your API key from the Developers page in your dashboard.

Authentication

Authenticate requests with an API key in the Authorization header.

Authorization: Bearer sk_live_xxxxx

API keys are created in your dashboard under Developers → API Keys. Keys start with sk_live_ and have configurable scopes.

Send Email

POST/api/send

Send a single transactional email. Supports HTML, plain text, templates, attachments, and scheduling.

Parameters

fromrequired
string
Sender address. Format: "Name <email>" or plain email.
torequired
string | string[]
Recipient email(s). Max 50.
subjectrequired*
string
Email subject. Required unless using a template.
html
string
HTML body. Max 256KB.
text
string
Plain-text body.
template
string
Template slug. Overrides subject/html/text.
variables
object
Template variables for {{ variable }} replacement.
reply_to
string
Reply-to address.
tags
string[]
Tags for filtering. Max 5, each max 50 chars.
metadata
object
Custom metadata stored with the email log.

Idempotency

Pass an Idempotency-Key header to safely retry requests. If the same key is sent within 24 hours, the cached result is returned.

const { data, error } = await send16.emails.send({
  from: 'Acme <hello@acme.com>',
  to: 'user@gmail.com',
  subject: 'Your Invoice #1234',
  html: '<p>Please find your invoice attached.</p>',
  tags: ['invoices'],
}, {
  idempotencyKey: 'inv-1234',
});

Response

{
  "success": true,
  "data": {
    "message_id": "d90270bb-7b22-4b59-90f7-3706a456d3cf",
    "log_id": "01JQXYZ..."
  }
}

Batch Send

POST/api/send-batch

Send up to 100 emails in a single request. Each email is processed independently.

const { data, error } = await send16.emails.batch([
  {
    from: 'Acme <hello@acme.com>',
    to: 'alice@example.com',
    subject: 'Hello Alice',
    html: '<p>Hi Alice!</p>',
  },
  {
    from: 'Acme <hello@acme.com>',
    to: 'bob@example.com',
    subject: 'Hello Bob',
    html: '<p>Hi Bob!</p>',
  },
]);

console.log(`${data.success}/${data.total} sent`);

Response

{
  "success": true,
  "data": {
    "total": 2,
    "success": 2,
    "failed": 0,
    "results": [
      { "success": true, "message_id": "...", "log_id": "..." },
      { "success": true, "message_id": "...", "log_id": "..." }
    ]
  }
}

Email Status

GET/api/emails/{log_id}

Check the delivery status of a sent email.

const { data, error } = await send16.emails.get('01JQXYZ...');

console.log(data.status); // 'delivered'

Response

{
  "success": true,
  "data": {
    "id": "01JQXYZ...",
    "to": "user@gmail.com",
    "subject": "Hello from Send16",
    "status": "delivered",
    "message_id": "d90270bb-...",
    "created_at": "2026-04-01T10:00:00Z",
    "sent_at": "2026-04-01T10:00:01Z",
    "delivered_at": "2026-04-01T10:00:03Z",
    "opened_at": null,
    "clicked_at": null
  }
}

Contacts

Manage your contact list programmatically.

Create Contact

POST/api/contacts
const { data, error } = await send16.contacts.create({
  email: 'jane@example.com',
  firstName: 'Jane',
  lastName: 'Doe',
});

List Contacts

GET/api/contacts
const { data } = await send16.contacts.list({ page: 1, limit: 50 });
console.log(data.total, 'contacts');

Update Contact

PATCH/api/contacts/{id}
const { data } = await send16.contacts.update('contact_id', {
  firstName: 'Janet',
  unsubscribed: false,
});

Delete Contact

DELETE/api/contacts/{id}
await send16.contacts.delete('contact_id');

Domains

Add and verify sending domains. DNS records are generated automatically.

Add Domain

POST/api/transactional/api/domains
const { data } = await send16.domains.create('mail.acme.com');
// Returns domain with DNS records to configure

Verify Domain

POST/api/transactional/api/domains/{id}/verify
const { data } = await send16.domains.verify('domain_id');
data.results.forEach(r => {
  console.log(r.purpose, r.verified ? 'OK' : 'MISSING');
});

List Domains

GET/api/transactional/api/domains
const { data } = await send16.domains.list();

Webhooks

Receive real-time notifications when email events occur. Configure webhook URLs in your dashboard under Settings.

Event Types

email.sentEmail accepted by mail server
email.deliveredEmail delivered to recipient inbox
email.bouncedEmail bounced (includes bounce_type and bounce_message)
email.complainedRecipient marked email as spam
email.openedRecipient opened the email
email.clickedRecipient clicked a link (includes url)
contact.createdNew contact added
contact.unsubscribedContact unsubscribed
form.submittedForm submission received

Verify Webhook Signatures

Every webhook includes an X-Webhook-Signature header. Verify it to ensure the request came from Send16.

import { Webhooks } from 'send16-mail';

const webhooks = new Webhooks('whsec_xxxxx');

app.post('/webhooks/send16', express.raw({ type: 'application/json' }), async (req, res) => {
  const signature = req.headers['x-webhook-signature'];

  try {
    const event = await webhooks.constructEvent(req.body.toString(), signature);

    switch (event.event) {
      case 'email.delivered':
        console.log('Delivered to:', event.data.to);
        break;
      case 'email.bounced':
        console.log('Bounced:', event.data.bounce_message);
        break;
    }

    res.json({ received: true });
  } catch (err) {
    res.status(400).json({ error: 'Invalid signature' });
  }
});

Error Handling

The SDK never throws on API errors. 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);  // Human-readable description
  return;
}

// data is guaranteed to be non-null here
console.log(data.message_id);

HTTP Status Codes

200 / 201Success
400Bad request — validation error, duplicate, etc.
401Unauthorized — invalid or missing API key
403Forbidden — API key missing required scope
404Not found
413Payload too large — HTML body exceeds 256KB
429Rate limit or email quota exceeded

REST API Error Format

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": ""to" is required"
  }
}

Framework Examples

Drop-in examples for popular frameworks.

Next.js (App Router)

// app/api/send/route.ts
import { Send16 } from 'send16-mail';

const send16 = new Send16(process.env.SEND16_API_KEY!);

export async function POST(req: Request) {
  const { to, subject, html } = await req.json();

  const { data, error } = await send16.emails.send({
    from: 'Acme <hello@acme.com>',
    to, subject, html,
  });

  if (error) return Response.json({ error: error.message }, { status: 500 });
  return Response.json(data);
}

Express

import express from 'express';
import { Send16 } from 'send16-mail';

const app = express();
const send16 = new Send16(process.env.SEND16_API_KEY);

app.post('/send', express.json(), async (req, res) => {
  const { data, error } = await send16.emails.send(req.body);
  if (error) return res.status(500).json({ error: error.message });
  res.json(data);
});

app.listen(3000);

Cloudflare Workers

import { Send16 } from 'send16-mail';

export default {
  async fetch(request, env) {
    const send16 = new Send16(env.SEND16_API_KEY);
    const { to, subject, html } = await request.json();

    const { data, error } = await send16.emails.send({
      from: 'Acme <hello@acme.com>',
      to, subject, html,
    });

    if (error) return Response.json({ error: error.message }, { status: 500 });
    return Response.json(data);
  },
};

Hono

import { Hono } from 'hono';
import { Send16 } from 'send16-mail';

const app = new Hono();
const send16 = new Send16(process.env.SEND16_API_KEY);

app.post('/send', async (c) => {
  const body = await c.req.json();
  const { data, error } = await send16.emails.send(body);
  if (error) return c.json({ error: error.message }, 500);
  return c.json(data);
});

export default app;

Need help? Email support@send16.com