`hono-email/smtp` provides a connector-based SMTP sender. You choose a runtime connector, instantiate `SmtpTransport`, and pass it to `sendEmail()`.

## Example

```tsx
import CloudflareConnector from 'hono-email/smtp/cloudflare'
import { Body, Html, Text, sendEmail } from 'hono-email'
import { SmtpTransport } from 'hono-email/smtp'

const dkimPrivateKey = `-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----`

const smtp = new SmtpTransport({
  connector: CloudflareConnector,
  hostname: 'smtp.example.com',
  port: 587,
  secure: 'starttls',
  connectionTimeout: 10_000,
  greetingTimeout: 10_000,
  socketTimeout: 30_000,
  auth: {
    username: 'smtp-user',
    password: 'smtp-password',
  },
  dkim: {
    domainName: 'example.com',
    keySelector: 'mail',
    privateKey: dkimPrivateKey,
  },
  pool: {
    maxConnections: 2,
    maxMessages: 100,
  },
  limits: {
    maxAttachmentSize: 10 * 1024 * 1024,
  },
})

try {
  await smtp.verify()

  const receipt = await sendEmail({
    adapter: smtp,
    from: 'sender@example.com',
    to: ['recipient@example.com', 'second@example.com'],
    subject: 'Welcome',
    envelope: {
      from: 'bounces@example.com',
    },
    jsx: (
      <Html>
        <Body>
          <Text>Hello from hono-email.</Text>
        </Body>
      </Html>
    ),
    attachments: [
      {
        filename: 'invoice.txt',
        content: 'Invoice text',
        contentType: 'text/plain',
      },
      {
        filename: 'logo.png',
        href: 'https://example.com/assets/logo.png',
        cid: 'logo',
        contentDisposition: 'inline',
      },
    ],
  })

  if (!receipt.successful) {
    console.error(receipt.errorMessages)
  }
} finally {
  await smtp.close()
}
```

For the full list of configuration options, see the [SMTP Options API Reference](/api/adapter-options/#smtp-options).

## Runtime connectors

| Entry point                  | Runtime            |
| ---------------------------- | ------------------ |
| `hono-email/smtp/cloudflare` | Cloudflare Workers |
| `hono-email/smtp/node`       | Node.js            |
| `hono-email/smtp/deno`       | Deno               |
| `hono-email/smtp/bun`        | Bun                |

## Important runtime notes

- SMTP transport uses Web Streams internally. Runtime-specific socket support is supplied by the connector.
- Cloudflare Workers does not allow outbound SMTP connections on port `25`. Use submission ports such as `465` or `587`.
- Bun's TCP API supports direct TLS connections, but the Bun connector does not support STARTTLS upgrade. Use port `465` with `secure: true` on Bun.

## Connection pooling

`SmtpTransport` reuses SMTP sessions until `smtp.close()` is called. The default pool size is `1`, so sends on the same transport share one TCP connection sequentially.

- Set `pool.maxConnections` to allow multiple concurrent SMTP sessions.
- Set `pool.maxMessages` to retire a session after a fixed number of messages.
- If a session fails during send, that session is discarded. The message is not retried automatically.

## Delivery controls

- `await smtp.verify()` checks connection setup, TLS negotiation, and authentication without sending a message.
- `dkim` can be configured on `SmtpTransport` or overridden per message to add a `DKIM-Signature` header before SMTP delivery.
- `envelope` lets you override the SMTP envelope sender and recipients without changing the visible `From` / `To` headers.
- `to`, `cc`, `bcc`, and `envelope.to` accept single addresses or arrays. SMTP sends one `RCPT TO` command per resolved recipient and reports partial recipient rejection in `receipt.rejected`.

## Options

import { Badge } from '@astrojs/starlight/components'

### `connector` <Badge text="Required" variant="caution" size="small" />

**Type:** `SmtpConnector`

Runtime-specific TCP socket connector. Import one for your runtime:

| Entry point                  | Runtime            |
| ---------------------------- | ------------------ |
| `hono-email/smtp/node`       | Node.js            |
| `hono-email/smtp/bun`        | Bun                |
| `hono-email/smtp/deno`       | Deno               |
| `hono-email/smtp/cloudflare` | Cloudflare Workers |

### `hostname` <Badge text="Required" variant="caution" size="small" />

**Type:** `string`

SMTP server hostname.

### `port` <Badge text="Required" variant="caution" size="small" />

**Type:** `number`

SMTP server port. Use `587` for STARTTLS submission, `465` for implicit TLS.

### `secure` <Badge text="Optional" variant="note" size="small" />

**Type:** `boolean | 'starttls'` · **Default:** `'starttls'`

TLS mode:

| Value        | Behavior                                       |
| ------------ | ---------------------------------------------- |
| `'starttls'` | Upgrade to TLS after connecting (STARTTLS).    |
| `true`       | Implicit TLS from the start of the connection. |
| `false`      | Plain-text connection with no TLS.             |

:::caution
Bun's TCP connector does not support STARTTLS. Use `secure: true` with port `465` on Bun.
:::

### `auth` <Badge text="Optional" variant="note" size="small" />

**Type:** `{ type?: 'plain' | 'login'; username: string; password: string }`

SMTP authentication credentials. `type` defaults to `'plain'`.

### `dkim` <Badge text="Optional" variant="note" size="small" />

**Type:** `EmailDkimOptions`

DKIM signing options. See [Email Types](/api/email-types/#emaildkimoptions).

### `clientName` <Badge text="Optional" variant="note" size="small" />

**Type:** `string`

Hostname sent in the EHLO/HELO greeting. Defaults to the connector's resolved hostname.

### `connectionTimeout` <Badge text="Optional" variant="note" size="small" />

**Type:** `number`

Milliseconds to wait for the TCP connection to be established.

### `greetingTimeout` <Badge text="Optional" variant="note" size="small" />

**Type:** `number`

Milliseconds to wait for the server greeting after connecting.

### `socketTimeout` <Badge text="Optional" variant="note" size="small" />

**Type:** `number`

Milliseconds of inactivity before the socket is considered timed out.

### `pool` <Badge text="Optional" variant="note" size="small" />

**Type:** `{ maxConnections?: number; maxMessages?: number }`

Connection pool settings.

- `maxConnections` — maximum concurrent SMTP sessions. Default: `1`.
- `maxMessages` — retire a session after this many messages. Default: unlimited.

### `limits.maxAttachmentSize` <Badge text="Optional" variant="note" size="small" />

**Type:** `number`

Maximum attachment size in bytes.
