Node.js SDK

The official delivrie npm package for Node.js and TypeScript: install, configuration, results and errors, retries, every method, attachments and webhook verification.

The delivrie package is the official SDK for Node.js and TypeScript. It has no dependencies and uses only web-standard APIs (fetch and Web Crypto), so the same code runs on Node.js 18+, Bun, Deno, Cloudflare Workers and Vercel Edge. Source: github.com/codeddesigner/delivrie.

npm

npm install delivrie

pnpm

pnpm add delivrie

yarn

yarn add delivrie

bun

bun add delivrie

Create a client

import { Delivrie } from "delivrie";

const delivrie = new Delivrie(process.env.DELIVRIE_API_KEY);

If you leave out the key, the client reads DELIVRIE_API_KEY from the environment. On edge runtimes without process.env (Cloudflare Workers, for example), pass the key from your bindings: new Delivrie(env.DELIVRIE_API_KEY).

CommonJS works too: const { Delivrie } = require("delivrie").

OptionDefaultWhat it does
baseUrlhttps://delivrie.io/apiAPI address
timeout30000Milliseconds before a request gives up
maxRetries2Retries for server errors and network failures
fetchglobal fetchYour own fetch, for tests or older runtimes
headersnoneExtra headers on every request
const delivrie = new Delivrie(process.env.DELIVRIE_API_KEY, { timeout: 10_000, maxRetries: 3 });

Send an email

const { data, error } = await delivrie.emails.send({
  from: "Acme <orders@mail.acme.com>",
  to: "ada@example.com",
  subject: "Your order is confirmed",
  html: "<p>Order <b>#1042</b> is on its way.</p>",
  replyTo: "support@acme.com",
  tags: [{ name: "category", value: "receipt" }],
}, { idempotencyKey: "order-1042-receipt" });

Field names match the API (reply_to, scheduled_at). replyTo and scheduledAt are accepted as aliases, and scheduledAt can be a Date. Every field is described in Send an email.

React Email

Pass a React Email component as react, and the SDK renders it to HTML and plain text for you:

npm install @react-email/components @react-email/render
import { Welcome } from "./emails/welcome";

await delivrie.emails.send({
  from: "Acme <hello@mail.acme.com>",
  to: "ada@example.com",
  subject: "Welcome to Acme",
  react: <Welcome name="Ada" />,
});

react works in emails.batch too. If you also pass html, the html wins. Without @react-email/render installed, the call returns an error named react_render_error explaining what to install. On edge runtimes that can't load it at run time, render first with await render(<Welcome />) and pass the result as html.

Results and errors

Every method resolves to { data, error }, and exactly one of them is null. API errors never throw, so there's no try/catch to forget:

const { data, error } = await delivrie.emails.send(email);
if (error) {
  console.error(error.statusCode, error.name, error.message);
  return;
}
console.log(data.id);

error.name is the API's error name, like validation_error or monthly_quota_exceeded (see Errors). Problems that never reach the API come back with statusCode: 0 and the name network_error, timeout or aborted.

Retries and idempotency

await delivrie.emails.send(email, { idempotencyKey: `welcome-${user.id}` });

Cancel a request with an AbortSignal: delivrie.emails.send(email, { signal: AbortSignal.timeout(5000) }).

Attachments

Pass content as a base64 string, a Node.js Buffer or a Uint8Array; the SDK encodes bytes for you. Up to 40 MB per email.

import { readFile } from "node:fs/promises";

await delivrie.emails.send({
  from: "Billing <billing@mail.acme.com>",
  to: "ada@example.com",
  subject: "Your invoice",
  text: "Your invoice is attached.",
  attachments: [{ filename: "invoice-1042.pdf", content: await readFile("invoice-1042.pdf") }],
});

Methods

ResourceMethods
emailssend(email, opts?), batch(emails, opts?), get(id), list({ limit, offset, direction }), listReceived(), update(id, { scheduled_at }), cancel(id), forward(id, { to, from, note })
domainscreate({ name }), get(id), verify(id), update(id, {...}), list(), remove(id)
apiKeyscreate({ name, permission, domain_id }), list(), remove(id)
webhookscreate({ endpoint, events }), list(), deliveries(id), test(id), replay(deliveryId), verify(opts)
contactscreate({...}), get(idOrEmail), update(idOrEmail, {...}), list({...}), remove(idOrEmail)
segmentscreate({ name }), list(), remove(id), addContacts(id, contacts)
topics, contactPropertieslist()
broadcastscreate({...}), get(id), list(), send(id, { scheduled_at }), cancel(id), recipients(id), remove(id)
templatescreate({...}), get(idOrAlias), list(), publish(id)
suppressionsadd(email | emails), list(), remove(email)
eventssend({ event, email, data }), list()
automationslist(), get(id), create({...}), runs(id), publish(id), pause(id), resume(id)
inbox.threadslist({...}), get(id), update(id, {...}), reply(id, { text | html })
inbox.labelslist()
metricsget({ days, domain_id })
logslist({ limit, offset }), get(id)
usageget()

For anything not wrapped yet, call the API directly with the same auth, retries and error handling:

const { data, error } = await delivrie.request("GET", "/emails/received", undefined, { query: { limit: 10 } });

Verify webhooks

verifyWebhook checks a webhook's signature against the raw request body and your whsec_ secret, rejects requests older than five minutes, and returns the parsed event. It's async because it uses Web Crypto.

import { verifyWebhook } from "delivrie";

export async function POST(req: Request) {
  try {
    const event = await verifyWebhook({
      payload: await req.text(),
      headers: req.headers,
      secret: process.env.DELIVRIE_WEBHOOK_SECRET!,
    });
    // event.type, event.data
    return new Response("ok");
  } catch {
    return new Response("invalid signature", { status: 400 });
  }
}

With Express, read the raw body with express.raw({ type: "application/json" }) and pass req.body and req.headers. More in Webhooks.

TypeScript

Types ship with the package: SendEmailRequest, Email, Domain, Contact, WebhookEvent, DelivrieErrorResponse and more.

import type { SendEmailRequest, WebhookEvent } from "delivrie";

More guides