Send16 API Reference
Send transactional emails with a simple API. Use our Node.js SDK or call the REST API directly from any language.
https://api.send16.comGetting Started
Install the SDK and send your first email in under a minute.
1. Install
npm install send16-mail2. 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);
}Authentication
Authenticate requests with an API key in the Authorization header.
Authorization: Bearer sk_live_xxxxxAPI keys are created in your dashboard under Developers → API Keys. Keys start with sk_live_ and have configurable scopes.
Send Email
/api/sendSend a single transactional email. Supports HTML, plain text, templates, attachments, and scheduling.
Parameters
fromrequiredtorequiredsubjectrequired*htmltexttemplatevariablesreply_totagsmetadataIdempotency
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
/api/send-batchSend 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
/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
/api/contactsconst { data, error } = await send16.contacts.create({
email: 'jane@example.com',
firstName: 'Jane',
lastName: 'Doe',
});List Contacts
/api/contactsconst { data } = await send16.contacts.list({ page: 1, limit: 50 });
console.log(data.total, 'contacts');Update Contact
/api/contacts/{id}const { data } = await send16.contacts.update('contact_id', {
firstName: 'Janet',
unsubscribed: false,
});Delete Contact
/api/contacts/{id}await send16.contacts.delete('contact_id');Domains
Add and verify sending domains. DNS records are generated automatically.
Add Domain
/api/transactional/api/domainsconst { data } = await send16.domains.create('mail.acme.com');
// Returns domain with DNS records to configureVerify Domain
/api/transactional/api/domains/{id}/verifyconst { data } = await send16.domains.verify('domain_id');
data.results.forEach(r => {
console.log(r.purpose, r.verified ? 'OK' : 'MISSING');
});List Domains
/api/transactional/api/domainsconst { 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 serveremail.deliveredEmail delivered to recipient inboxemail.bouncedEmail bounced (includes bounce_type and bounce_message)email.complainedRecipient marked email as spamemail.openedRecipient opened the emailemail.clickedRecipient clicked a link (includes url)contact.createdNew contact addedcontact.unsubscribedContact unsubscribedform.submittedForm submission receivedVerify 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 / 201Success400Bad request — validation error, duplicate, etc.401Unauthorized — invalid or missing API key403Forbidden — API key missing required scope404Not found413Payload too large — HTML body exceeds 256KB429Rate limit or email quota exceededREST 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