# Adapter options (/docs/infrastructure/email/adapters)

---
title: Adapter options
description: Every constructor option on the four built-in mail adapters, what each one defaults to, and the Ethereal throwaway inbox for tests.
kind: guide
package: questpie
---

[Email](/docs/infrastructure/email) covers picking an adapter and swapping one.
Everything below goes in the same slot, `email.adapter` in
`questpie.config.ts`, and none of it changes a template or a send call.

## Console

```ts
import { ConsoleAdapter } from "questpie/adapters/console";

new ConsoleAdapter(); // both options are optional
new ConsoleAdapter({ logHtml: true });
```

| Option    | Type                        | Default       | Notes                                                             |
| --------- | --------------------------- | ------------- | ----------------------------------------------------------------- |
| `logHtml` | `boolean`                   | `false`       | When `false` the HTML body becomes a one-line hint. HTML is long. |
| `logger`  | `(message: string) => void` | `console.log` | Where every line goes.                                            |

It prints From, To, CC, BCC, Reply-To, Subject, the plain-text body, attachment
filenames, and the HTML only when you ask. It never sends.

## SMTP

```ts
import { SmtpAdapter } from "questpie/adapters/smtp";

new SmtpAdapter({
	transport: {
		host: "smtp.example.com",
		port: 587,
		secure: false,
		auth: { user: env.SMTP_USER, pass: env.SMTP_PASS },
	},
});

new SmtpAdapter({ transport: "smtp://user:pass@smtp.example.com:587" });
```

| Option              | Type                                               | Notes                                                      |
| ------------------- | -------------------------------------------------- | ---------------------------------------------------------- |
| `transport`         | `SMTPTransport \| SMTPTransport.Options \| string` | Handed straight to `nodemailer.createTransport`. Required. |
| `afterSendCallback` | `(info: SentMessageInfo) => void \| Promise<void>` | Runs after each send, with nodemailer's info object.       |

`SerializableMailOptions` is passed to `sendMail` unchanged, so CC, BCC,
attachments, `replyTo` and custom headers all behave the way nodemailer
documents them. `SmtpAdapter` also exposes `verify(): Promise<boolean>`, which
proxies nodemailer's connection check.

### Ethereal test inbox

`createEtherealSmtpAdapter()` creates a throwaway
[Ethereal](https://ethereal.email) account and returns an `SmtpAdapter` already
wired to log a preview URL after every send.

```ts
import { createEtherealSmtpAdapter } from "questpie/adapters/smtp";

export default runtimeConfig({
	email: { adapter: createEtherealSmtpAdapter() }, // a Promise<SmtpAdapter>
});
```

It returns a promise, which `MailerConfig.adapter` accepts as-is because the
mailer awaits the adapter on send. Dev and tests only. It makes a network call
to create the account.

## Resend

```ts
import { resendAdapter } from "questpie/adapters/resend";

resendAdapter({ apiKey: env.RESEND_API_KEY });
```

| Option              | Type                                                               | Default                     | Notes                                                      |
| ------------------- | ------------------------------------------------------------------ | --------------------------- | ---------------------------------------------------------- |
| `apiKey`            | `string`                                                           | required                    | A Resend key, or one for a Resend-compatible provider.     |
| `baseUrl`           | `string`                                                           | `"https://api.resend.com"`  | Point at a compatible API. Trailing slashes are stripped.  |
| `userAgent`         | `string`                                                           | `"questpie-resend-adapter"` | Resend wants a `User-Agent` on direct HTTP calls.          |
| `idempotencyKey`    | `string \| ((options) => string \| undefined)`                     | none                        | Static or per-message. Sets the `Idempotency-Key` header.  |
| `fetch`             | `typeof fetch`                                                     | global `fetch`              | Override for tests or a non-standard runtime.              |
| `afterSendCallback` | `(response: ResendSendResponse, options) => void \| Promise<void>` | none                        | Gets the parsed JSON body, so the message `id` lands here. |

It POSTs to `<baseUrl>/emails`, maps `replyTo` to `reply_to`, and base64-encodes
`Buffer` attachments. A non-2xx response throws
`Resend API error (<status> <statusText>): <body>`.

## Plunk

```ts
import { plunkAdapter } from "questpie/adapters/plunk";

plunkAdapter({ apiKey: env.PLUNK_SECRET_KEY });
```

| Option              | Type                                                              | Default                           | Notes                                                      |
| ------------------- | ----------------------------------------------------------------- | --------------------------------- | ---------------------------------------------------------- |
| `apiKey`            | `string`                                                          | required                          | A Plunk **secret** key. Public keys only track events.     |
| `baseUrl`           | `string`                                                          | `"https://next-api.useplunk.com"` | Override for self-hosted Plunk.                            |
| `fromName`          | `string`                                                          | none                              | Applied only when `from` is a bare address, no `Name <…>`. |
| `subscribed`        | `boolean`                                                         | none                              | Subscribes recipients to marketing. Leave it off.          |
| `fetch`             | `typeof fetch`                                                    | global `fetch`                    | Override for tests or a non-standard runtime.              |
| `afterSendCallback` | `(response: PlunkSendResponse, options) => void \| Promise<void>` | none                              | Gets the parsed body once Plunk accepts the message.       |

It POSTs to `<baseUrl>/v1/send` with `body` set to `html || text` and `reply`
carrying `replyTo`. It base64-encodes `Buffer` attachments, same as Resend, and
sends string attachment content through unchanged.

<Callout type="warn" title="Two failure modes, not one">
	Plunk throws on a non-2xx response, and also on a 2xx whose body carries
	`success: false`. Either way the message is a `Plunk API error` line with the
	status and Plunk's own error code. A `cc` or `bcc` throws before the request.
</Callout>

## Related

- [Email](/docs/infrastructure/email), the adapter slot, the swap, and the `MailAdapter` interface.
- [Emails](/docs/code/emails), the templates and send calls above all of this.
- [Environment](/docs/ship/environment), typed `env()` for API keys and SMTP credentials.
