Tutorials
Sending emails with Node.js
A complete Node.js walkthrough with the official SDK: sending, error handling and retries.
Use the official notideus — zero dependencies, Node 18 and later:
npm install notideus
The send function
emails.send authenticates with your API key, throws a typed NotideusError on failure, and resolves with the stored email row:
import { Notideus } from 'notideus';
const notideus = new Notideus(process.env.NT_KEY); // "nt_live_…"
const email = await notideus.emails.send({
from: 'Notideus <noreply@demo.dev>',
to: ['jane@example.com'],
subject: 'Welcome to Notideus',
html: '<p>Hi Jane,</p><p>Your account is ready.</p>',
idempotency_key: 'docs-quickstart-1'
});
console.log(email.id, email.status);
const API_KEY = process.env.NT_KEY; // "nt_live_…"
const BASE = "https://api.notideus.io/v1";
async function send(payload) {
const res = await fetch(`${BASE}/emails`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (!res.ok) {
const body = await res.json().catch(() => ({}));
const err = new Error(
body?.error?.message ?? `request failed with ${res.status}`,
);
err.status = res.status;
err.code = body?.error?.code;
throw err;
}
return res.json();
}
const email = await send({
from: "Notideus <noreply@demo.dev>",
to: ["jane@example.com"],
subject: "Welcome to Notideus",
html: "<p>Hi Jane,</p><p>Your account is ready.</p>",
});
console.log(email.id, email.status);
Expected output
send resolves with the stored email row (201 Created). Response abbreviated to id, status, from, to and subject:
{
"id": "01a0a11c-188e-7c9e-9b69-753fcb4c8d0d",
"status": "queued",
"from_address": "Notideus <noreply@demo.dev>",
"to_emails": ["jane@example.com"],
"subject": "Welcome to Notideus"
}
id uniquely identifies this email; status is queued — it is picked up for delivery from there.
Error handling
The SDK throws NotideusError, which carries the API error code, the HTTP status, and retryAfter (seconds) on 429. Catch it and branch on err.code:
import { Notideus, NotideusError } from 'notideus';
try {
await notideus.emails.send({
from: 'Notideus <noreply@demo.dev>',
to: ['jane@example.com'],
subject: 'Welcome to Notideus',
html: '<p>Hi Jane,</p><p>Your account is ready.</p>'
});
} catch (err) {
if (err instanceof NotideusError) {
console.error(err.code, err.status, err.message);
}
}
| Status | Code | What to do |
|---|---|---|
| 401 | unauthorized | Missing, invalid or revoked key — check NT_KEY. |
| 403 | insufficient_scope | The key lacks emails:send — create a key with the right scope. |
| 402 | quota_exceeded | Monthly allowance reached — back off and retry later. See Quotas and limits. |
| 422 | from_domain_not_verified | The From domain matches no verified team domain — verify it in the dashboard first. |
The SDK retries idempotent sends for you: it re-issues a request on network failure, 5xx or 429 (honoring
Retry-After) only when the send carries an idempotency_key — so a retried send never double-sends. Choosing the key is still yours: derive it from your own data (for example order-12345-welcome). See Idempotency for key choice and scope rules.