Unosend
Transports

Unosend

Send transactional emails via the Unosend HTTP API

The Unosend transport is an official Better-Notify package that sends transactional email through the Unosend HTTP API. It uses plain fetch() with zero external dependencies, so it works in Node.js, Bun, Cloudflare Workers, and any runtime with a global fetch.

Install

npm install @betternotify/unosend @betternotify/core @betternotify/email

Getting your API key

  1. Sign up or log in at unosend.co.
  2. Go to API Keys in your dashboard.
  3. Create a new API key and copy it — it starts with un_.

Store it as an environment variable:

UNOSEND_API_KEY=un_xxxxxxxxxxxxxxxxxxxxxxxxx

You must verify your sending domain before sending emails through Unosend.

Usage

import { createNotify, createClient } from '@betternotify/core';
import { emailChannel } from '@betternotify/email';
import { unosendTransport } from '@betternotify/unosend';

const email = emailChannel({
  defaults: { from: { name: 'My App', email: 'noreply@example.com' } },
});

const rpc = createNotify({ channels: { email } });
const catalog = rpc.catalog({
  /* routes */
});

const mail = createClient({
  catalog,
  transportsByChannel: {
    email: unosendTransport({
      apiKey: process.env.UNOSEND_API_KEY!,
    }),
  },
});

Options

Prop

Type

Error handling

The transport maps Unosend HTTP status codes to Better-Notify error codes:

HTTP statusBetter-Notify codeRetriable
400VALIDATIONNo
401CONFIGNo
403CONFIGNo
422VALIDATIONNo
429RATE_LIMITEDYes
500+PROVIDERYes

Network failures and unparseable responses are wrapped as PROVIDER errors. Timeouts are wrapped as TIMEOUT errors.

Per-send overrides

Unosend supports priority, scheduled sending, and tracking toggles as per-send overrides via TransportDataMap:

await mail.welcome.send({
  to: 'user@example.com',
  input: { name: 'Alice' },
  transport: {
    unosend: {
      priority: 'high',
      scheduled_for: '2026-06-01T10:00:00Z',
      tracking: { open: false, click: true },
    },
  },
});

Prop

Type

Tags

Better-Notify's tags field (a Record<string, string | number | boolean>) is converted to Unosend's Array<{ name, value }> format automatically:

await mail.welcome.send({
  to: 'user@example.com',
  input: { name: 'Alice' },
  tags: { campaign: 'onboarding', version: 2 },
});
// Sent as: [{ name: "campaign", value: "onboarding" }, { name: "version", value: "2" }]

Attachments

The transport base64-encodes attachment content before sending:

await mail.invoice.send({
  to: 'user@example.com',
  input: { orderId: '12345' },
  attachments: [
    { filename: 'invoice.pdf', content: pdfBuffer, contentType: 'application/pdf' },
  ],
});

Examples

On this page