> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mailchannels.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Nodemailer

> Send email through Nodemailer using the MailChannels transport.

The `mailchannels-sdk` package ships a [Nodemailer](https://nodemailer.com/) transport. If you already have a Node.js
application that composes messages with Nodemailer, you can switch to MailChannels by swapping the transport and leaving
the rest of your code alone.

> [!NOTE]
>
> This transport is optional and lives alongside the rest of the JavaScript SDK. It requires the `nodemailer` package,
> which the SDK does not install for you. The transport itself can only send mail. It doesn't implement the full API
> surface (sub-accounts, keys, webhooks, etc.). For those features, use the SDK's `MailChannels` client directly.

## Prerequisites

Before you send your first message, follow the [JavaScript quickstart](https://docs.mailchannels.com/email-api/javascript/quickstart.md#prerequisites)
prerequisite section. This will walk you through creating an account, generating an API key, and adding the required DNS
records.

## Installation

Install the SDK and Nodemailer:

```bash theme={null}
npm i mailchannels-sdk nodemailer
```

The transport requires Node.js 20 or later, the same as the SDK.

### Configuring the transport

Create a Nodemailer transporter with `mailchannelsTransport()`, imported from `mailchannels-sdk/nodemailer`:

```javascript theme={null}
import nodemailer from 'nodemailer';
import { mailchannelsTransport } from 'mailchannels-sdk/nodemailer';

const transporter = nodemailer.createTransport(
  mailchannelsTransport({
    apiKey: process.env.MAILCHANNELS_API_KEY,
    sendMode: 'async', // 'async' (default) or 'sync'
  })
);
```

The transport accepts the following options:

| Option | Description |
| - | - |
| `apiKey` | Your MailChannels API key. Required. |
| `sendMode` | `async` (the default) queues the message and returns as soon as MailChannels accepts it. `sync` waits for the regular send path and reports a per-message result. See [Send vs send async](https://docs.mailchannels.com/email-api/send-vs-send-async.md). |
| `baseUrl`, `timeout`, `signal`, `retry` | Passed through to the underlying SDK client. Omit them for the defaults. |

## Sending mail

Once configured, send mail through Nodemailer's standard `sendMail()`. No MailChannels-specific code is required for a
basic send:

```javascript theme={null}
const info = await transporter.sendMail({
  from: 'sender@example.com',
  to: 'recipient@example.com',
  subject: 'Hello from MailChannels',
  text: 'Plain-text body.',
  html: '<p>HTML body.</p>',
});
```

See Nodemailer's docs on [message configuration](https://nodemailer.com/message/) for everything else `sendMail()`
accepts. The transport maps `from`, `to`, `cc`, `bcc`, `replyTo`, `subject`, `text`, `html`, `headers`, `attachments`,
`icalEvent`, and `dkim` onto the corresponding Email API fields.

> [!NOTE]
>
> Every address in `to`, `cc`, and `bcc` receives the same message. If you need to send multiple individualized emails
> in one request, call the SDK's `MailChannels` client directly instead of going through Nodemailer. See
> [How emails are structured](https://docs.mailchannels.com/email-api/how-emails-are-structured.md) for more information on personalizations.

### MailChannels options

Nodemailer's message object has no equivalent for some MailChannels API features. Reach those instead through the
`mailchannels` field on the `sendMail()` options. The SDK augments Nodemailer's types, so these are type-checked in
TypeScript projects:

```javascript theme={null}
await transporter.sendMail({
  from: 'sender@example.com',
  to: 'recipient@example.com',
  subject: 'Hello from MailChannels',
  html: '<p>HTML body.</p>',
  mailchannels: {
    transactional: true,
    campaignId: 'welcome-2026',
  },
});
```

| Option | Description |
| - | - |
| `campaignId` | Campaign that this message is part of. |
| `tracking` | Enable open and click tracking: `{ open: { enable: true }, click: { enable: true } }`. Add `customDomainName` to either to route links through a registered custom tracking domain. See [Custom tracking](https://docs.mailchannels.com/email-api/custom-tracking.md). |
| `transactional` | Whether the mail is transactional or non-transactional (marketing, etc.). Affects List-Unsubscribe handling, see [Unsubscribe](https://docs.mailchannels.com/email-api/unsubscribe.md). |
| `unsubscribe` | `{ customDomainName }`, the name of a registered custom tracking domain to use for unsubscribe links. See [Custom tracking](https://docs.mailchannels.com/email-api/custom-tracking.md). |

Each of these corresponds directly to a field on the [send endpoint](https://docs.mailchannels.com/api-reference/send/send-an-email.md). See that
reference for full semantics.

### DKIM

Nodemailer's `dkim` message option is passed to MailChannels, which signs the message on your behalf. Supply a single
signature with `domainName`, `keySelector`, and `privateKey`:

```javascript theme={null}
await transporter.sendMail({
  // ...
  dkim: {
    domainName: 'example.com',
    keySelector: 'mailchannels',
    privateKey: process.env.DKIM_PRIVATE_KEY,
  },
});
```

> [!WARNING]
>
> Use an RSA private key; 2048 bits is recommended. Every mailbox provider can verify RSA signatures, and RSA is the key
> type MailChannels can validate when checking your domain's DKIM setup. Nodemailer's multi-signature `dkim.keys`
> form is rejected. A `privateKey` given as a `{ key, passphrase }` object is decrypted and converted to PEM before
> sending.
>
> See [DKIM signing and key management](https://docs.mailchannels.com/email-api/configuring-dkim.md) for information on how MailChannels handles
> signing and keys.

## Reading the result

`sendMail()` resolves with Nodemailer's usual `info` object. What it contains depends on `sendMode`:

* Synchronous send (`sendMode: 'sync'`): `messageId` is the ID MailChannels assigned, and every recipient appears in
  `accepted` or `rejected` depending on the send result.
* Queued (default): the API doesn't return a message ID per call, so `messageId` is `null` and both `accepted` and
  `rejected` are empty. Delivery status arrives later through [webhooks](https://docs.mailchannels.com/email-api/webhooks.md).

In both modes, `info.response` holds the raw SDK response.

```javascript theme={null}
const info = await transporter.sendMail(message);
console.log(info.messageId, info.accepted, info.response);
```

## Error handling

Any SDK error raised while sending (authentication, validation, rate limiting, server errors) is surfaced as a rejected
promise carrying the API's error message:

```javascript theme={null}
try {
  await transporter.sendMail(message);
} catch (error) {
  console.error('Send failed:', error.message);
}
```

To inspect the original API error, pass a callback instead. The `info` argument is still populated on failure, and
`info.response.error` carries the error's `type`, `statusCode`, and `message`:

```javascript theme={null}
transporter.sendMail(message, (error, info) => {
  if (error) {
    console.error('Send failed:', info.response?.error);
    return;
  }
  console.log('Sent:', info.messageId);
});
```

> [!NOTE]
>
> A missing `apiKey` is reported when the transport is created, not when you send. `mailchannelsTransport()` throws
> `Missing MailChannels API key.` immediately.

## Limitations

* **One message per call.** Every recipient gets the same content; there is no per-recipient template data or header
  override through this transport.
* **Single reply-to address.** If `replyTo` is an array, only the first address is used.
* **DKIM only via the MailChannels API**, as described above.
* **No Mustache templating.** This transport has no way to use MailChannels' Mustache template rendering. Use the SDK's
  `MailChannels` client directly; see [Templates](https://docs.mailchannels.com/email-api/templates.md) for how.

## Next steps

* Read the [webhooks guide](https://docs.mailchannels.com/email-api/webhooks.md) to learn how to get real-time notifications about email
  delivery, bounces, and complaints.
* Explore the [API reference](https://docs.mailchannels.com/email-api/api-reference-introduction.md) to see what else you can do with the API.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.