mailchannels-sdk 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/mailchannels-js/SKILL.md +4 -0
- package/.agents/skills/mailchannels-js/resources/attachments.md +8 -10
- package/.agents/skills/mailchannels-js/resources/clients-and-transport.md +5 -5
- package/.agents/skills/mailchannels-js/resources/custom-headers.md +4 -4
- package/.agents/skills/mailchannels-js/resources/dkim.md +10 -10
- package/.agents/skills/mailchannels-js/resources/domain-checks.md +6 -6
- package/.agents/skills/mailchannels-js/resources/error-handling.md +5 -5
- package/.agents/skills/mailchannels-js/resources/metrics-and-usage.md +9 -9
- package/.agents/skills/mailchannels-js/resources/overview.md +5 -5
- package/.agents/skills/mailchannels-js/resources/plugins/nodemailer.md +112 -0
- package/.agents/skills/mailchannels-js/resources/sending.md +10 -10
- package/.agents/skills/mailchannels-js/resources/sub-accounts.md +7 -9
- package/.agents/skills/mailchannels-js/resources/suppressions.md +4 -4
- package/.agents/skills/mailchannels-js/resources/templates.md +5 -5
- package/.agents/skills/mailchannels-js/resources/testing.md +52 -5
- package/.agents/skills/mailchannels-js/resources/unsubscribe.md +3 -3
- package/.agents/skills/mailchannels-js/resources/webhooks.md +8 -8
- package/README.md +73 -2
- package/dist/_chunks/mailchannels.d.mts +2094 -0
- package/dist/_chunks/mailchannels.mjs +2201 -0
- package/dist/_chunks/simulator.mjs +16 -8
- package/dist/cli/index.mjs +268 -0
- package/dist/mailchannels.d.mts +1 -2095
- package/dist/mailchannels.mjs +1 -2201
- package/dist/plugins/nodemailer/index.d.mts +33 -0
- package/dist/plugins/nodemailer/index.mjs +145 -0
- package/dist/simulator/index.d.mts +20 -0
- package/dist/simulator/index.mjs +2 -0
- package/package.json +21 -9
- package/dist/cli.d.mts +0 -1
- package/dist/cli.mjs +0 -50
|
@@ -4,9 +4,7 @@ Sub-accounts are first-class on MailChannels. Use them for tenants, customers, o
|
|
|
4
4
|
senders so that one customer's reputation, limits, and bad traffic don't contaminate the
|
|
5
5
|
parent account or other tenants.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
### Handles
|
|
7
|
+
## Handles
|
|
10
8
|
|
|
11
9
|
A handle uniquely identifies a sub-account. Rules:
|
|
12
10
|
|
|
@@ -15,7 +13,7 @@ A handle uniquely identifies a sub-account. Rules:
|
|
|
15
13
|
- Unique per parent account.
|
|
16
14
|
- If omitted on create, a random handle is generated.
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
## Lifecycle
|
|
19
17
|
|
|
20
18
|
```ts
|
|
21
19
|
import { MailChannels } from 'mailchannels-sdk'
|
|
@@ -43,7 +41,7 @@ const { error: deleteError } = await mc.subAccounts.delete('clienta')
|
|
|
43
41
|
if (deleteError) { /*...*/ }
|
|
44
42
|
```
|
|
45
43
|
|
|
46
|
-
|
|
44
|
+
## Credentials
|
|
47
45
|
|
|
48
46
|
Each sub-account has its own API keys and SMTP passwords.
|
|
49
47
|
|
|
@@ -76,7 +74,7 @@ create time. Store it immediately or rotate. Each sub-account has server-side ca
|
|
|
76
74
|
many API keys and SMTP passwords it can hold — once at the cap, create returns
|
|
77
75
|
`unprocessable_entity_error`; delete an unused credential first.
|
|
78
76
|
|
|
79
|
-
|
|
77
|
+
## Limits
|
|
80
78
|
|
|
81
79
|
Per-sub-account monthly send caps. A sub-account without a limit inherits the parent's
|
|
82
80
|
capacity.
|
|
@@ -92,7 +90,7 @@ const { error: deleteLimitErr } = await mc.subAccounts.limits.delete('clienta')
|
|
|
92
90
|
if (deleteLimitErr) { /*...*/ }
|
|
93
91
|
```
|
|
94
92
|
|
|
95
|
-
|
|
93
|
+
## Usage
|
|
96
94
|
|
|
97
95
|
```ts
|
|
98
96
|
const { data: parentUsage } = await mc.metrics.usage()
|
|
@@ -104,7 +102,7 @@ console.log(parentUsage?.total, parentUsage?.startDate, parentUsage?.endDate)
|
|
|
104
102
|
`mc.metrics.usage()` is for the parent account. Use `mc.subAccounts.getUsage(handle)` for
|
|
105
103
|
one specific sub-account.
|
|
106
104
|
|
|
107
|
-
|
|
105
|
+
## Sending As A Sub-Account
|
|
108
106
|
|
|
109
107
|
Create a separate `MailChannels` instance with the sub-account's API key:
|
|
110
108
|
|
|
@@ -123,7 +121,7 @@ if (error) { /*...*/ }
|
|
|
123
121
|
This keeps the account boundary explicit in code and avoids hard-to-debug issues where the
|
|
124
122
|
wrong key is used at the wrong call site.
|
|
125
123
|
|
|
126
|
-
|
|
124
|
+
## Suppressions And Sub-Accounts
|
|
127
125
|
|
|
128
126
|
When creating suppressions on the parent, set `addToSubAccounts: true` to also copy entries
|
|
129
127
|
into every sub-account. See [suppressions](suppressions.md).
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
A suppression list keeps known-bad or opted-out recipients out of future sends. MailChannels
|
|
4
4
|
suppresses by recipient + suppression type + source.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
## Create Entries
|
|
7
7
|
|
|
8
8
|
```ts
|
|
9
9
|
import { MailChannels } from 'mailchannels-sdk'
|
|
@@ -38,7 +38,7 @@ Constraints:
|
|
|
38
38
|
All entries created via this endpoint have an inherent source of `'api'`.
|
|
39
39
|
The endpoint does not have a field to set the source value.
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
## List Entries
|
|
42
42
|
|
|
43
43
|
```ts
|
|
44
44
|
const { data, error } = await mc.suppressions.list({
|
|
@@ -67,7 +67,7 @@ const { data: recent } = await mc.suppressions.list({
|
|
|
67
67
|
|
|
68
68
|
Date formats accepted: `YYYY-MM-DD`, `YYYY-MM-DDTHH:MM:SSZ`, or a `Date` object.
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
## Delete An Entry
|
|
71
71
|
|
|
72
72
|
Warning:
|
|
73
73
|
Do not remove entries from the suppression list if the recipient has not explicitly opted back in.
|
|
@@ -81,7 +81,7 @@ const { error: delAllErr } = await mc.suppressions.delete('recipient@example.net
|
|
|
81
81
|
If `source` is omitted it defaults to `'api'`. Use `'all'` to remove every suppression for
|
|
82
82
|
that recipient regardless of origin.
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
## Patterns
|
|
85
85
|
|
|
86
86
|
- **Preference center opt-out**: set `types` according to the email category, e.g. `non-transactional` for marketing
|
|
87
87
|
emails, `transactional` for order updates, and so on.
|
|
@@ -8,7 +8,7 @@ no "create template" endpoint. To use a template:
|
|
|
8
8
|
|
|
9
9
|
The only supported template type is `'mustache'`.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Single Recipient
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
14
|
import { MailChannels } from 'mailchannels-sdk'
|
|
@@ -28,7 +28,7 @@ const { data, error } = await mc.emails.queue({
|
|
|
28
28
|
})
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
## Multiple Recipients With Per-Recipient Variables
|
|
32
32
|
|
|
33
33
|
Each personalization renders independently with its own variables. Root-level
|
|
34
34
|
`template.data` is the base; per-personalization `template.data` is merged on top
|
|
@@ -56,7 +56,7 @@ const { data, error } = await mc.emails.queue({
|
|
|
56
56
|
})
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
## Allowed Template Variable Value Types
|
|
60
60
|
|
|
61
61
|
Keys are strings. Values may be:
|
|
62
62
|
|
|
@@ -69,12 +69,12 @@ Keys are strings. Values may be:
|
|
|
69
69
|
`null`, `undefined`, and class instances are rejected with a `validation_error` before
|
|
70
70
|
the request leaves the client.
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
## Subject Templates
|
|
73
73
|
|
|
74
74
|
The root `subject` field is **also** mustache-rendered when a `template` is set. There is
|
|
75
75
|
no separate subject template configuration.
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
## Preview Without Sending
|
|
78
78
|
|
|
79
79
|
Use `dryRun: true` on `emails.send()` to render and validate without delivering:
|
|
80
80
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The SDK ships a built-in API simulator that runs a local HTTP server mimicking the real
|
|
4
4
|
MailChannels API. Use it for integration tests without hitting the live API.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
## Starting The Simulator (CLI)
|
|
7
7
|
|
|
8
8
|
Start the simulator as a background process before your test run:
|
|
9
9
|
|
|
@@ -25,12 +25,12 @@ alongside your test runner:
|
|
|
25
25
|
"test:integration": "start-server-and-test 'npx mailchannels-sdk simulate --silent' http://localhost:8787 vitest"
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
### Example Test (Vitest / Jest)
|
|
28
|
+
### Example Test: CLI (Vitest / Jest)
|
|
29
29
|
|
|
30
30
|
Point the `MailChannels` client at the running simulator via `baseUrl`:
|
|
31
31
|
|
|
32
32
|
```ts
|
|
33
|
-
import {
|
|
33
|
+
import { it, expect } from 'vitest'
|
|
34
34
|
import { MailChannels } from 'mailchannels-sdk'
|
|
35
35
|
|
|
36
36
|
// Simulator must already be running: npx mailchannels-sdk simulate --port 8787 --silent
|
|
@@ -49,7 +49,54 @@ it('queues an email', async () => {
|
|
|
49
49
|
})
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
## Starting The Simulator (Programmatic)
|
|
53
|
+
|
|
54
|
+
Import `createSimulator` from the `mailchannels-sdk/simulator` entrypoint and start the
|
|
55
|
+
simulator directly within your code. This gives you deterministic control over its
|
|
56
|
+
lifecycle, making it ideal for integration tests that need a fresh simulator instance
|
|
57
|
+
per run or when running in CI without a separate background process.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { createSimulator } from 'mailchannels-sdk/simulator'
|
|
61
|
+
|
|
62
|
+
const simulator = createSimulator({ port: 8787, silent: true })
|
|
63
|
+
const simulatorUrl = await simulator.listen()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Example Test: Programmatic (Vitest / Jest)
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { afterAll, beforeAll, expect, it } from 'vitest'
|
|
70
|
+
import { createSimulator } from 'mailchannels-sdk/simulator'
|
|
71
|
+
import { MailChannels } from 'mailchannels-sdk'
|
|
72
|
+
|
|
73
|
+
let simulator: ReturnType<typeof createSimulator>
|
|
74
|
+
let mc: MailChannels
|
|
75
|
+
|
|
76
|
+
beforeAll(async () => {
|
|
77
|
+
simulator = createSimulator({ port: 8787, silent: true })
|
|
78
|
+
const simulatorUrl = await simulator.listen()
|
|
79
|
+
mc = new MailChannels('test-key', { baseUrl: simulatorUrl })
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
afterAll(async () => {
|
|
83
|
+
await simulator.close()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('queues an email', async () => {
|
|
87
|
+
const { data, error } = await mc.emails.queue({
|
|
88
|
+
from: 'sender@example.com',
|
|
89
|
+
to: 'recipient@example.net',
|
|
90
|
+
subject: 'Test',
|
|
91
|
+
text: 'Hello'
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
expect(error).toBeNull()
|
|
95
|
+
expect(data?.requestId).toBeDefined()
|
|
96
|
+
})
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Client-Side Validation Without A Simulator
|
|
53
100
|
|
|
54
101
|
The SDK validates payloads before making any HTTP call — `validation_error` results are
|
|
55
102
|
returned synchronously (no network required). Tests for client-side validation rules don't
|
|
@@ -73,7 +120,7 @@ it('rejects reserved headers', async () => {
|
|
|
73
120
|
|
|
74
121
|
No network call is made because the error is caught before `_fetch` runs.
|
|
75
122
|
|
|
76
|
-
|
|
123
|
+
## Dry Run For Template Assertions
|
|
77
124
|
|
|
78
125
|
Use `emails.send(options, true)` (dry-run) against the real API in a staging pipeline to
|
|
79
126
|
assert templates render correctly before shipping:
|
|
@@ -10,7 +10,7 @@ inbox providers (Gmail, Yahoo, etc.) effectively require both for bulk senders.
|
|
|
10
10
|
Both mechanisms require the message to have **exactly one recipient per personalization**
|
|
11
11
|
and to be **DKIM-signed**.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## In-Body Unsubscribe Link
|
|
14
14
|
|
|
15
15
|
Use the literal placeholder string `{{mc-unsubscribe-url}}` inside a mustache HTML body.
|
|
16
16
|
MailChannels substitutes a hosted one-click unsubscribe URL at render time.
|
|
@@ -30,7 +30,7 @@ const { data, error } = await mc.emails.queue({
|
|
|
30
30
|
|
|
31
31
|
The `template` field must be present for `{{mc-unsubscribe-url}}` to be substituted.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
## `List-Unsubscribe` Headers (Non-Transactional)
|
|
34
34
|
|
|
35
35
|
Setting `transactional: false` tells MailChannels to add `List-Unsubscribe` and
|
|
36
36
|
`List-Unsubscribe-Post` headers automatically. These headers are what inbox providers read
|
|
@@ -60,7 +60,7 @@ const { data, error } = await mc.emails.queue({
|
|
|
60
60
|
If `transactional: false` is set but a personalization has more than one recipient,
|
|
61
61
|
the SDK returns a `validation_error` before making any HTTP call.
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
## When To Use Which
|
|
64
64
|
|
|
65
65
|
| Message type | In-body link | `transactional: false` headers |
|
|
66
66
|
| --- | --- | --- |
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
MailChannels posts batched delivery events to a URL you register. Events cover both
|
|
4
4
|
`emails.send()` and `emails.queue()` sends and use the same payload shape.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
## Enroll And Manage
|
|
7
7
|
|
|
8
8
|
```ts
|
|
9
9
|
import { MailChannels } from 'mailchannels-sdk'
|
|
@@ -25,7 +25,7 @@ Enroll the replacement first if you're swapping URLs.
|
|
|
25
25
|
|
|
26
26
|
If the endpoint is already enrolled, `create()` returns `conflict_error`.
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
## Validate
|
|
29
29
|
|
|
30
30
|
`mc.webhooks.validate()` sends a synthetic test request to **every** enrolled webhook and
|
|
31
31
|
reports each one's response. Useful as a deploy check.
|
|
@@ -47,7 +47,7 @@ for (const entry of data?.results ?? []) {
|
|
|
47
47
|
|
|
48
48
|
The test payload carries `event: 'test'` and a hardcoded sender of `test@mailchannels.com`.
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
## Inspect Batches
|
|
51
51
|
|
|
52
52
|
`mc.webhooks.batches()` returns up to 500 batch summaries with status, status code,
|
|
53
53
|
duration, and event count. Use it to investigate failed deliveries.
|
|
@@ -72,7 +72,7 @@ const { data: recent } = await mc.webhooks.batches({
|
|
|
72
72
|
Time formats accepted: `YYYY-MM-DD`, `YYYY-MM-DDTHH:MM:SSZ`, or a `Date` object. If neither `createdAfter`
|
|
73
73
|
nor `createdBefore` is set, the default range is the last 3 days.
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
## Resend A Batch
|
|
76
76
|
|
|
77
77
|
```ts
|
|
78
78
|
const { data, error } = await mc.webhooks.resendBatch(12345)
|
|
@@ -83,7 +83,7 @@ console.log(data?.statusCode, data?.duration)
|
|
|
83
83
|
A successful call means the resend attempt completed — not that your webhook returned 2xx.
|
|
84
84
|
Check `data.statusCode` to see what your endpoint actually returned.
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
## Verify Incoming Webhooks (Crucial)
|
|
87
87
|
|
|
88
88
|
MailChannels signs every webhook request with an Ed25519 signature. `Webhooks.verify()`
|
|
89
89
|
does the full verification — content digest, freshness, and signature — in one call.
|
|
@@ -126,7 +126,7 @@ instance (no API key needed). It is also available as an instance method on
|
|
|
126
126
|
the signature will never match. Use `req.body.toString()` (Express with raw middleware),
|
|
127
127
|
`await request.text()` (Fetch API / Hono / Cloudflare Workers), or the framework equivalent.
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
### Supplying The Public Key Manually
|
|
130
130
|
|
|
131
131
|
By default `verify()` fetches and caches the public key automatically from MailChannels.
|
|
132
132
|
You can supply it yourself to avoid the outbound call:
|
|
@@ -148,7 +148,7 @@ Cache the key — it only changes on rotation, and MailChannels may publish mult
|
|
|
148
148
|
keys at once during a rollover. Always fetch by the `keyId` from the incoming request
|
|
149
149
|
rather than holding a single "current" key.
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
## Event Payload Shape
|
|
152
152
|
|
|
153
153
|
After successful verification `data` is typed as an array of webhook events. Common shared
|
|
154
154
|
fields:
|
|
@@ -167,7 +167,7 @@ fields:
|
|
|
167
167
|
| `reason` | `string?` | A human readable explanation of the status code. Do not use for system logic. Present on `hard-bounced`, `soft-bounced` events. |
|
|
168
168
|
| `url` / `userAgent` / `ip` | `string?` | Present on `click` / `open` events. |
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
## Responding
|
|
171
171
|
|
|
172
172
|
Return any 2xx status code quickly. MailChannels treats anything else as a failure and may
|
|
173
173
|
retry. Keep your handler thin: enqueue the event and return, then do the actual processing
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[![TypeScript][typescript-src]][typescript-href]
|
|
10
10
|
[![Node.js][node-src]][node-href]
|
|
11
11
|
|
|
12
|
-
> Built and tested against Email API `1.
|
|
12
|
+
> Built and tested against Email API `1.6.0`
|
|
13
13
|
|
|
14
14
|
Node.js SDK to integrate [MailChannels Email API](https://docs.mailchannels.com/email-api) into your JavaScript or TypeScript server-side applications.
|
|
15
15
|
|
|
@@ -28,6 +28,7 @@ This library provides a simple way to interact with the [MailChannels Email API]
|
|
|
28
28
|
- 📚 [Usage](#usage)
|
|
29
29
|
- 📐 [Naming Conventions](#naming-conventions)
|
|
30
30
|
- 🧪 [Local simulator](#local-simulator)
|
|
31
|
+
- 📬 [Using with Nodemailer](#using-with-nodemailer)
|
|
31
32
|
- 🤖 [Using with an AI agent](#using-with-an-ai-agent)
|
|
32
33
|
- ⚖️ [License](#license)
|
|
33
34
|
- 💻 [Development](#development)
|
|
@@ -120,11 +121,22 @@ This package includes a local MailChannels simulator you can run via the CLI. It
|
|
|
120
121
|
|
|
121
122
|
### Start the simulator
|
|
122
123
|
|
|
124
|
+
CLI:
|
|
125
|
+
|
|
123
126
|
```sh
|
|
124
127
|
# default: http://127.0.0.1:8787
|
|
125
128
|
npx mailchannels-sdk simulate
|
|
126
129
|
```
|
|
127
130
|
|
|
131
|
+
Programmatically:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { createSimulator } from 'mailchannels-sdk/simulator'
|
|
135
|
+
|
|
136
|
+
const simulator = createSimulator()
|
|
137
|
+
const simulatorUrl = await simulator.listen()
|
|
138
|
+
```
|
|
139
|
+
|
|
128
140
|
### Options
|
|
129
141
|
|
|
130
142
|
| Option | Description | Default |
|
|
@@ -185,6 +197,65 @@ const { data, error } = await mailchannels.emails.send({
|
|
|
185
197
|
The next planned expansion is outbound webhook delivery so client applications can test webhook ingestion flows against the simulator as well.
|
|
186
198
|
<!-- #endregion simulator -->
|
|
187
199
|
|
|
200
|
+
## <a name="using-with-nodemailer">📬 Using with Nodemailer</a>
|
|
201
|
+
|
|
202
|
+
The SDK provides a transport for Nodemailer that allows you to send emails using the MailChannels Email API.
|
|
203
|
+
|
|
204
|
+
### Install
|
|
205
|
+
|
|
206
|
+
```sh
|
|
207
|
+
# npm
|
|
208
|
+
npm i mailchannels-sdk nodemailer && npm i -D @types/nodemailer
|
|
209
|
+
|
|
210
|
+
# yarn
|
|
211
|
+
yarn add mailchannels-sdk nodemailer && yarn add -D @types/nodemailer
|
|
212
|
+
|
|
213
|
+
# pnpm
|
|
214
|
+
pnpm add mailchannels-sdk nodemailer && pnpm add -D @types/nodemailer
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Sending
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
import nodemailer from 'nodemailer'
|
|
221
|
+
import { mailchannelsTransport } from 'mailchannels-sdk/nodemailer'
|
|
222
|
+
|
|
223
|
+
const transport = nodemailer.createTransport(
|
|
224
|
+
mailchannelsTransport({
|
|
225
|
+
apiKey: 'your-api-key',
|
|
226
|
+
sendMode: 'async', // 'async' or 'sync'. Default is 'async'
|
|
227
|
+
// SDK client options: `baseUrl`, `timeout`, `signal`, `retry`
|
|
228
|
+
})
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
transport.sendMail({
|
|
232
|
+
from: 'sender@example.com',
|
|
233
|
+
to: 'recipient@example.com',
|
|
234
|
+
subject: 'Hello from Nodemailer',
|
|
235
|
+
html: '<p>Hello World</p>',
|
|
236
|
+
mailchannels: {
|
|
237
|
+
// SDK send options: `campaignId`, `tracking`, `transactional`, `unsubscribe`
|
|
238
|
+
}
|
|
239
|
+
}, (error, info) => {
|
|
240
|
+
if (error) {
|
|
241
|
+
console.error('Error sending email:', error)
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
console.log('Sent message info:', info)
|
|
245
|
+
})
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Limitations
|
|
249
|
+
|
|
250
|
+
The MailChannels Nodemailer transport maps a subset of Nodemailer features to the MailChannels SDK. Some features are not supported or have limitations:
|
|
251
|
+
|
|
252
|
+
- Only a single `replyTo` address is supported
|
|
253
|
+
- Multiple DKIM signatures are not supported
|
|
254
|
+
- For async sends (`sendMode: 'async'`), the response will have a `messageId` of `null`, and both the `accepted` and `rejected` arrays will be empty
|
|
255
|
+
- Attachment `path`, `href` and other URL fields are not supported
|
|
256
|
+
- MailChannels-specific send options must be passed via the augmented `mailchannels` field in the `sendMail` options
|
|
257
|
+
- Some advanced Nodemailer behaviors may be ignored or transformed when mapped to the SDK
|
|
258
|
+
|
|
188
259
|
## <a name="using-with-an-ai-agent">🤖 Using with an AI agent</a>
|
|
189
260
|
|
|
190
261
|
This repository ships a complete agent skill at
|
|
@@ -273,7 +344,7 @@ pnpm test:watch
|
|
|
273
344
|
# Run typecheck
|
|
274
345
|
pnpm test:types
|
|
275
346
|
|
|
276
|
-
# Refresh API parity fixtures
|
|
347
|
+
# Refresh API parity fixtures and README version note
|
|
277
348
|
pnpm parity:fixtures
|
|
278
349
|
|
|
279
350
|
# Run the local simulator
|