mailchannels-sdk 0.3.6
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/LICENSE +21 -0
- package/README.md +159 -0
- package/dist/mailchannels.d.mts +16 -0
- package/dist/mailchannels.d.ts +16 -0
- package/dist/mailchannels.mjs +54 -0
- package/dist/modules.d.mts +2 -0
- package/dist/modules.d.ts +2 -0
- package/dist/modules.mjs +1358 -0
- package/dist/shared/mailchannels-sdk.BDB65ygx.d.mts +1136 -0
- package/dist/shared/mailchannels-sdk.BDB65ygx.d.ts +1136 -0
- package/package.json +76 -0
package/dist/modules.mjs
ADDED
|
@@ -0,0 +1,1358 @@
|
|
|
1
|
+
var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
2
|
+
ErrorCode2[ErrorCode2["BadRequest"] = 400] = "BadRequest";
|
|
3
|
+
ErrorCode2[ErrorCode2["Unauthorized"] = 401] = "Unauthorized";
|
|
4
|
+
ErrorCode2[ErrorCode2["Forbidden"] = 403] = "Forbidden";
|
|
5
|
+
ErrorCode2[ErrorCode2["NotFound"] = 404] = "NotFound";
|
|
6
|
+
ErrorCode2[ErrorCode2["Conflict"] = 409] = "Conflict";
|
|
7
|
+
ErrorCode2[ErrorCode2["PayloadTooLarge"] = 413] = "PayloadTooLarge";
|
|
8
|
+
ErrorCode2[ErrorCode2["UnprocessableEntity"] = 422] = "UnprocessableEntity";
|
|
9
|
+
return ErrorCode2;
|
|
10
|
+
})(ErrorCode || {});
|
|
11
|
+
const getStatusError = (response, errors = {}) => {
|
|
12
|
+
return errors[response.status] || response._data?.message || "Unknown error.";
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const parseRecipientString = (input) => {
|
|
16
|
+
const trimmed = input.trim();
|
|
17
|
+
const match = trimmed.match(/^([^<]*)<([^@\s]+@[^>\s]+)>$/);
|
|
18
|
+
if (match) {
|
|
19
|
+
const [, name, email] = match;
|
|
20
|
+
return { email: email?.trim() || "", name: name?.trim() };
|
|
21
|
+
}
|
|
22
|
+
return { email: trimmed };
|
|
23
|
+
};
|
|
24
|
+
const parseRecipient = (recipient) => {
|
|
25
|
+
if (typeof recipient === "string") {
|
|
26
|
+
return parseRecipientString(recipient);
|
|
27
|
+
}
|
|
28
|
+
if (recipient?.email) {
|
|
29
|
+
return { email: recipient.email, name: recipient.name };
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const parseArrayRecipients = (recipients) => {
|
|
33
|
+
if (!recipients) return;
|
|
34
|
+
if (typeof recipients === "string") {
|
|
35
|
+
return [parseRecipientString(recipients)];
|
|
36
|
+
}
|
|
37
|
+
if (Array.isArray(recipients)) {
|
|
38
|
+
return recipients.map((recipient) => parseRecipient(recipient)).filter((recipient) => Boolean(recipient));
|
|
39
|
+
}
|
|
40
|
+
return [recipients];
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const stripPemHeaders = (pem) => pem.replace(/-----[^-]+-----|\s|#.*$/gm, "");
|
|
44
|
+
|
|
45
|
+
class Emails {
|
|
46
|
+
constructor(mailchannels) {
|
|
47
|
+
this.mailchannels = mailchannels;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Send an email using MailChannels Email API.
|
|
51
|
+
* @param options - The email options to send.
|
|
52
|
+
* @param dryRun - When set to `true`, the message will not be sent. Instead, the fully rendered message will be returned in the `data` property of the response. The default value is `false`.
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
56
|
+
* const { success } = await mailchannels.emails.send({
|
|
57
|
+
* to: 'to@example.com',
|
|
58
|
+
* from: 'from@example.com',
|
|
59
|
+
* subject: 'Test',
|
|
60
|
+
* html: 'Test'
|
|
61
|
+
* })
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
async send(options, dryRun = false) {
|
|
65
|
+
const { cc, bcc, from, to, html, text, mustaches, dkim } = options;
|
|
66
|
+
const data = { success: false, error: null };
|
|
67
|
+
const parsedFrom = parseRecipient(from);
|
|
68
|
+
if (!parsedFrom || !parsedFrom.email) {
|
|
69
|
+
data.error = "No sender provided. Use the `from` option to specify a sender";
|
|
70
|
+
return data;
|
|
71
|
+
}
|
|
72
|
+
const parsedTo = parseArrayRecipients(to);
|
|
73
|
+
if (!parsedTo || !parsedTo.length) {
|
|
74
|
+
data.error = "No recipients provided. Use the `to` option to specify at least one recipient";
|
|
75
|
+
return data;
|
|
76
|
+
}
|
|
77
|
+
if (!text && !html) {
|
|
78
|
+
data.error = "No email content provided";
|
|
79
|
+
return data;
|
|
80
|
+
}
|
|
81
|
+
const content = [];
|
|
82
|
+
const template_type = mustaches ? "mustache" : void 0;
|
|
83
|
+
if (text) content.push({ type: "text/plain", value: text, template_type });
|
|
84
|
+
if (html) content.push({ type: "text/html", value: html, template_type });
|
|
85
|
+
const payload = {
|
|
86
|
+
attachments: options.attachments,
|
|
87
|
+
personalizations: [{
|
|
88
|
+
bcc: parseArrayRecipients(bcc),
|
|
89
|
+
cc: parseArrayRecipients(cc),
|
|
90
|
+
to: parsedTo,
|
|
91
|
+
dkim_domain: dkim?.domain || void 0,
|
|
92
|
+
dkim_private_key: dkim?.privateKey ? stripPemHeaders(dkim.privateKey) : void 0,
|
|
93
|
+
dkim_selector: dkim?.selector || void 0,
|
|
94
|
+
dynamic_template_data: options.mustaches
|
|
95
|
+
}],
|
|
96
|
+
reply_to: parseRecipient(options.replyTo),
|
|
97
|
+
from: parsedFrom,
|
|
98
|
+
subject: options.subject,
|
|
99
|
+
content,
|
|
100
|
+
tracking_settings: options.tracking ? {
|
|
101
|
+
click_tracking: options.tracking.click ? { enable: options.tracking.click } : void 0,
|
|
102
|
+
open_tracking: options.tracking.open ? { enable: options.tracking.open } : void 0
|
|
103
|
+
} : void 0
|
|
104
|
+
};
|
|
105
|
+
const response = await this.mailchannels.post("/tx/v1/send", {
|
|
106
|
+
query: { "dry-run": dryRun },
|
|
107
|
+
body: payload,
|
|
108
|
+
onResponse: async ({ response: response2 }) => {
|
|
109
|
+
if (response2.ok) {
|
|
110
|
+
data.success = true;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
data.error = getStatusError(response2, {
|
|
114
|
+
[ErrorCode.BadRequest]: "Bad Request.",
|
|
115
|
+
[ErrorCode.Forbidden]: "User does not have access to this feature.",
|
|
116
|
+
[ErrorCode.PayloadTooLarge]: "The total message size should not exceed 20MB. This includes the message itself, headers, and the combined size of any attachments."
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}).catch(() => null);
|
|
120
|
+
data.data = response?.data;
|
|
121
|
+
return data;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Validates a domain's email authentication setup by retrieving its DKIM, SPF, and Domain Lockdown status. This endpoint checks whether the domain is properly configured for secure email delivery.
|
|
125
|
+
* @param options - The domain options to check.
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
129
|
+
* const { results } = await mailchannels.emails.checkDomain({
|
|
130
|
+
* dkim: [{
|
|
131
|
+
* domain: 'example.com',
|
|
132
|
+
* privateKey: 'your-private-key',
|
|
133
|
+
* selector: 'mailchannels'
|
|
134
|
+
* }],
|
|
135
|
+
* domain: 'example.com',
|
|
136
|
+
* senderId: 'sender-id'
|
|
137
|
+
* })
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
async checkDomain(options) {
|
|
141
|
+
const { dkim, domain, senderId } = options;
|
|
142
|
+
const dkimOptions = Array.isArray(dkim) ? dkim : [dkim];
|
|
143
|
+
const data = { results: null, error: null };
|
|
144
|
+
const payload = {
|
|
145
|
+
dkim_settings: dkimOptions.map(({ domain: domain2, privateKey, selector }) => ({
|
|
146
|
+
dkim_domain: domain2,
|
|
147
|
+
dkim_private_key: stripPemHeaders(privateKey),
|
|
148
|
+
dkim_selector: selector
|
|
149
|
+
})),
|
|
150
|
+
domain,
|
|
151
|
+
sender_id: senderId
|
|
152
|
+
};
|
|
153
|
+
const response = await this.mailchannels.post("/tx/v1/check-domain", {
|
|
154
|
+
body: payload,
|
|
155
|
+
onResponseError: async ({ response: response2 }) => {
|
|
156
|
+
data.error = getStatusError(response2, {
|
|
157
|
+
[ErrorCode.BadRequest]: "Bad Request.",
|
|
158
|
+
[ErrorCode.Forbidden]: "User does not have access to this feature."
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}).catch(() => null);
|
|
162
|
+
if (!response) return data;
|
|
163
|
+
data.results = {
|
|
164
|
+
dkim: response.check_results.dkim.map(({ dkim_domain, dkim_selector, reason, verdict }) => ({
|
|
165
|
+
domain: dkim_domain,
|
|
166
|
+
selector: dkim_selector,
|
|
167
|
+
reason,
|
|
168
|
+
verdict
|
|
169
|
+
})),
|
|
170
|
+
domainLockdown: response.check_results.domain_lockdown,
|
|
171
|
+
spf: response.check_results.spf,
|
|
172
|
+
references: response.references
|
|
173
|
+
};
|
|
174
|
+
return data;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
class Webhooks {
|
|
179
|
+
constructor(mailchannels) {
|
|
180
|
+
this.mailchannels = mailchannels;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Enrolls the customer to receive event notifications via webhooks.
|
|
184
|
+
* @param endpoint - The URL to receive event notifications.
|
|
185
|
+
* @example
|
|
186
|
+
* ```ts
|
|
187
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
188
|
+
* const { success } = mailchannels.webhooks.enroll('https://example.com/api/webhooks/mailchannels')
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
async enroll(endpoint) {
|
|
192
|
+
const data = { success: false, error: null };
|
|
193
|
+
if (!endpoint) {
|
|
194
|
+
data.error = "No endpoint provided.";
|
|
195
|
+
return data;
|
|
196
|
+
}
|
|
197
|
+
await this.mailchannels.post("/tx/v1/webhook", {
|
|
198
|
+
query: {
|
|
199
|
+
endpoint
|
|
200
|
+
},
|
|
201
|
+
ignoreResponseError: true,
|
|
202
|
+
onResponse: async ({ response }) => {
|
|
203
|
+
if (response.ok) {
|
|
204
|
+
data.success = true;
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
data.error = getStatusError(response, {
|
|
208
|
+
[ErrorCode.Conflict]: `Endpoint '${endpoint}' is already enrolled to receive notifications.`
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
return data;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Retrieves all registered webhook endpoints associated with the customer.
|
|
216
|
+
* @example
|
|
217
|
+
* ```ts
|
|
218
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
219
|
+
* const { webhooks } = await mailchannels.webhooks.list()
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
async list() {
|
|
223
|
+
const data = { webhooks: [], error: null };
|
|
224
|
+
const response = await this.mailchannels.get("/tx/v1/webhook", {
|
|
225
|
+
onResponseError: async ({ response: response2 }) => {
|
|
226
|
+
data.error = getStatusError(response2);
|
|
227
|
+
}
|
|
228
|
+
}).catch(() => []);
|
|
229
|
+
data.webhooks = response.map(({ webhook }) => webhook);
|
|
230
|
+
return data;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Deletes all registered webhook endpoints for the customer.
|
|
234
|
+
* @example
|
|
235
|
+
* ```ts
|
|
236
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
237
|
+
* const { success } = await mailchannels.webhooks.delete()
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
async delete() {
|
|
241
|
+
const data = { success: false, error: null };
|
|
242
|
+
await this.mailchannels.delete("/tx/v1/webhook", {
|
|
243
|
+
ignoreResponseError: true,
|
|
244
|
+
onResponse: async ({ response }) => {
|
|
245
|
+
if (!response.ok) {
|
|
246
|
+
data.error = getStatusError(response);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
data.success = true;
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
return data;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Retrieves the public key used to verify signatures on incoming webhook payloads.
|
|
256
|
+
* @param id - The ID of the key.
|
|
257
|
+
* @example
|
|
258
|
+
* ```ts
|
|
259
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
260
|
+
* const { key } = await mailchannels.webhooks.getSigningKey('key-id')
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
async getSigningKey(id) {
|
|
264
|
+
const data = { key: null, error: null };
|
|
265
|
+
const response = await this.mailchannels.get("/tx/v1/webhook/public-key", {
|
|
266
|
+
query: {
|
|
267
|
+
id
|
|
268
|
+
},
|
|
269
|
+
onResponseError: ({ response: response2 }) => {
|
|
270
|
+
data.error = getStatusError(response2, {
|
|
271
|
+
[ErrorCode.BadRequest]: "Bad Request.",
|
|
272
|
+
[ErrorCode.NotFound]: `The key '${id}' is not found.`
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}).catch(() => null);
|
|
276
|
+
data.key = response?.key || null;
|
|
277
|
+
return data;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
class SubAccounts {
|
|
282
|
+
constructor(mailchannels) {
|
|
283
|
+
this.mailchannels = mailchannels;
|
|
284
|
+
}
|
|
285
|
+
static HANDLE_PATTERN = /^[a-z0-9]{3,128}$/;
|
|
286
|
+
/**
|
|
287
|
+
* Creates a new sub-account under the parent account. Each sub-account must have a unique handle composed solely of lowercase alphanumeric characters. If no handle is provided, a random handle will be generated.
|
|
288
|
+
* @param handle - The handle of the sub-account to create. Sub-account handle must match the pattern `[a-z0-9]{3,128}`.
|
|
289
|
+
* @example
|
|
290
|
+
* ```ts
|
|
291
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
292
|
+
* const { account } = await mailchannels.subAccounts.create('validhandle123')
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
async create(handle) {
|
|
296
|
+
const data = { account: null, error: null };
|
|
297
|
+
if (handle) {
|
|
298
|
+
const isValidHandle = SubAccounts.HANDLE_PATTERN.test(handle);
|
|
299
|
+
if (!isValidHandle) {
|
|
300
|
+
data.error = "Invalid handle. Sub-account handle must match the pattern [a-z0-9]{3,128}";
|
|
301
|
+
return data;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const response = await this.mailchannels.post("/tx/v1/sub-account", {
|
|
305
|
+
body: handle ? { handle } : void 0,
|
|
306
|
+
onResponseError: ({ response: response2 }) => {
|
|
307
|
+
data.error = getStatusError(response2, {
|
|
308
|
+
[ErrorCode.Forbidden]: "The parent account does not have permission to create sub-accounts.",
|
|
309
|
+
[ErrorCode.Conflict]: `Sub-account with handle '${handle}' already exists.`
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}).catch(() => null);
|
|
313
|
+
data.account = response;
|
|
314
|
+
return data;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Retrieves all sub-accounts associated with the parent account. The response is paginated with a default limit of 1000 sub-accounts per page and an offset of 0.
|
|
318
|
+
* @param options - The options to filter the list of sub-accounts.
|
|
319
|
+
* @example
|
|
320
|
+
* ```ts
|
|
321
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
322
|
+
* const { accounts } = await mailchannels.subAccounts.list()
|
|
323
|
+
* ```
|
|
324
|
+
*/
|
|
325
|
+
async list(options) {
|
|
326
|
+
const data = { accounts: [], error: null };
|
|
327
|
+
if (typeof options?.limit === "number" && (options.limit < 1 || options.limit > 1e3)) {
|
|
328
|
+
data.error = "The limit value is invalid. Possible limit values are 1 to 1000.";
|
|
329
|
+
return data;
|
|
330
|
+
}
|
|
331
|
+
if (typeof options?.offset === "number" && options.offset < 0) {
|
|
332
|
+
data.error = "Offset must be greater than or equal to 0.";
|
|
333
|
+
return data;
|
|
334
|
+
}
|
|
335
|
+
const response = await this.mailchannels.get("/tx/v1/sub-account", {
|
|
336
|
+
query: options,
|
|
337
|
+
onResponseError: async ({ response: response2 }) => {
|
|
338
|
+
data.error = getStatusError(response2);
|
|
339
|
+
}
|
|
340
|
+
}).catch(() => []);
|
|
341
|
+
data.accounts = response;
|
|
342
|
+
return data;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Deletes the sub-account identified by its handle.
|
|
346
|
+
* @param handle - Handle of sub-account to be deleted.
|
|
347
|
+
* ```ts
|
|
348
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
349
|
+
* const { success } = await mailchannels.subAccounts.delete('validhandle123')
|
|
350
|
+
* ```
|
|
351
|
+
*/
|
|
352
|
+
async delete(handle) {
|
|
353
|
+
const data = { success: false, error: null };
|
|
354
|
+
if (!handle) {
|
|
355
|
+
data.error = "No handle provided.";
|
|
356
|
+
return data;
|
|
357
|
+
}
|
|
358
|
+
await this.mailchannels.delete(`/tx/v1/sub-account/${handle}`, {
|
|
359
|
+
ignoreResponseError: true,
|
|
360
|
+
onResponse: async ({ response }) => {
|
|
361
|
+
if (!response.ok) {
|
|
362
|
+
data.error = getStatusError(response);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
data.success = true;
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
return data;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Suspends the sub-account identified by its handle. This action disables the account, preventing it from sending any emails until it is reactivated.
|
|
372
|
+
* @param handle - Handle of sub-account to be suspended.
|
|
373
|
+
* @example
|
|
374
|
+
* ```ts
|
|
375
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
376
|
+
* const { success } = await mailchannels.subAccounts.suspend('validhandle123')
|
|
377
|
+
* ```
|
|
378
|
+
*/
|
|
379
|
+
async suspend(handle) {
|
|
380
|
+
const data = { success: false, error: null };
|
|
381
|
+
if (!handle) {
|
|
382
|
+
data.error = "No handle provided.";
|
|
383
|
+
return data;
|
|
384
|
+
}
|
|
385
|
+
await this.mailchannels.post(`/tx/v1/sub-account/${handle}/suspend`, {
|
|
386
|
+
ignoreResponseError: true,
|
|
387
|
+
onResponse: async ({ response }) => {
|
|
388
|
+
if (response.ok) {
|
|
389
|
+
data.success = true;
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
data.error = getStatusError(response, {
|
|
393
|
+
[ErrorCode.NotFound]: `The specified sub-account '${handle}' does not exist.`
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
return data;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Activates a suspended sub-account identified by its handle, restoring its ability to send emails.
|
|
401
|
+
* @param handle - Handle of sub-account to be activated.
|
|
402
|
+
* @example
|
|
403
|
+
* ```ts
|
|
404
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
405
|
+
* const { success } = await mailchannels.subAccounts.activate('validhandle123')
|
|
406
|
+
* ```
|
|
407
|
+
*/
|
|
408
|
+
async activate(handle) {
|
|
409
|
+
const data = { success: false, error: null };
|
|
410
|
+
if (!handle) {
|
|
411
|
+
data.error = "No handle provided.";
|
|
412
|
+
return data;
|
|
413
|
+
}
|
|
414
|
+
await this.mailchannels.post(`/tx/v1/sub-account/${handle}/activate`, {
|
|
415
|
+
ignoreResponseError: true,
|
|
416
|
+
onResponse: async ({ response }) => {
|
|
417
|
+
if (response.ok) {
|
|
418
|
+
data.success = true;
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
data.error = getStatusError(response, {
|
|
422
|
+
[ErrorCode.Forbidden]: "The parent account does not have permission to activate the sub-account.",
|
|
423
|
+
[ErrorCode.NotFound]: `The specified sub-account '${handle}' does not exist.`
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
return data;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Creates a new API key for the specified sub-account.
|
|
431
|
+
* @param handle - Handle of the sub-account to create API key for.
|
|
432
|
+
* @example
|
|
433
|
+
* ```ts
|
|
434
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
435
|
+
* const { key } = await mailchannels.subAccounts.createApiKey('validhandle123')
|
|
436
|
+
* ```
|
|
437
|
+
*/
|
|
438
|
+
async createApiKey(handle) {
|
|
439
|
+
const data = { key: null, error: null };
|
|
440
|
+
if (!handle) {
|
|
441
|
+
data.error = "No handle provided.";
|
|
442
|
+
return data;
|
|
443
|
+
}
|
|
444
|
+
const response = await this.mailchannels.post(`/tx/v1/sub-account/${handle}/api-key`, {
|
|
445
|
+
onResponseError: async ({ response: response2 }) => {
|
|
446
|
+
data.error = getStatusError(response2, {
|
|
447
|
+
[ErrorCode.Forbidden]: "You can't create API keys for this sub-account.",
|
|
448
|
+
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`,
|
|
449
|
+
[ErrorCode.UnprocessableEntity]: "You have reached the limit of API keys you can create for this sub-account."
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
}).catch(() => null);
|
|
453
|
+
if (!response) return data;
|
|
454
|
+
data.key = {
|
|
455
|
+
id: response.id,
|
|
456
|
+
value: response.key
|
|
457
|
+
};
|
|
458
|
+
return data;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Retrieves details of all API keys associated with the specified sub-account. For security reasons, the full API key is not returned; only the key ID and a partially redacted version are provided.
|
|
462
|
+
* @param handle - Handle of the sub-account to retrieve the API key for.
|
|
463
|
+
* @example
|
|
464
|
+
* ```ts
|
|
465
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
466
|
+
* const { keys } = await mailchannels.subAccounts.listApiKeys('validhandle123')
|
|
467
|
+
* ```
|
|
468
|
+
*/
|
|
469
|
+
async listApiKeys(handle) {
|
|
470
|
+
const data = { keys: [], error: null };
|
|
471
|
+
if (!handle) {
|
|
472
|
+
data.error = "No handle provided.";
|
|
473
|
+
return data;
|
|
474
|
+
}
|
|
475
|
+
const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/api-key`, {
|
|
476
|
+
onResponseError: async ({ response: response2 }) => {
|
|
477
|
+
data.error = getStatusError(response2, {
|
|
478
|
+
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}).catch(() => []);
|
|
482
|
+
data.keys = response.map((key) => ({
|
|
483
|
+
id: key.id,
|
|
484
|
+
value: key.key
|
|
485
|
+
}));
|
|
486
|
+
return data;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Deletes the API key identified by its ID for the specified sub-account.
|
|
490
|
+
* @param handle - Handle of the sub-account for which the API key should be deleted.
|
|
491
|
+
* @param id - The ID of the API key to delete.
|
|
492
|
+
* @example
|
|
493
|
+
* ```ts
|
|
494
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
495
|
+
* const { success } = await mailchannels.subAccounts.deleteApiKey('validhandle123', 1)
|
|
496
|
+
* ```
|
|
497
|
+
*/
|
|
498
|
+
async deleteApiKey(handle, id) {
|
|
499
|
+
const data = { success: false, error: null };
|
|
500
|
+
if (!handle) {
|
|
501
|
+
data.error = "No handle provided.";
|
|
502
|
+
return data;
|
|
503
|
+
}
|
|
504
|
+
await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/api-key/${id}`, {
|
|
505
|
+
ignoreResponseError: true,
|
|
506
|
+
onResponse: async ({ response }) => {
|
|
507
|
+
if (response.ok) {
|
|
508
|
+
data.success = true;
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
data.error = getStatusError(response, {
|
|
512
|
+
[ErrorCode.BadRequest]: "Missing or invalid API key ID."
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
return data;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Creates a new SMTP password for the specified sub-account.
|
|
520
|
+
* @param handle - Handle of the sub-account to create SMTP password for.
|
|
521
|
+
* @example
|
|
522
|
+
* ```ts
|
|
523
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
524
|
+
* const { password } = await mailchannels.subAccounts.createSmtpPassword('validhandle123')
|
|
525
|
+
* ```
|
|
526
|
+
*/
|
|
527
|
+
async createSmtpPassword(handle) {
|
|
528
|
+
const data = { password: null, error: null };
|
|
529
|
+
if (!handle) {
|
|
530
|
+
data.error = "No handle provided.";
|
|
531
|
+
return data;
|
|
532
|
+
}
|
|
533
|
+
const response = await this.mailchannels.post(`/tx/v1/sub-account/${handle}/smtp-password`, {
|
|
534
|
+
onResponseError: async ({ response: response2 }) => {
|
|
535
|
+
data.error = getStatusError(response2, {
|
|
536
|
+
[ErrorCode.Forbidden]: "You can't create SMTP passwords for this sub-account.",
|
|
537
|
+
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`,
|
|
538
|
+
[ErrorCode.UnprocessableEntity]: "You have reached the limit of SMTP passwords you can create for this sub-account."
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
}).catch(() => null);
|
|
542
|
+
if (!response) return data;
|
|
543
|
+
data.password = {
|
|
544
|
+
enabled: response.enabled,
|
|
545
|
+
id: response.id,
|
|
546
|
+
value: response.smtp_password
|
|
547
|
+
};
|
|
548
|
+
return data;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Retrieves details of all SMTP passwords associated with the specified sub-account. For security, the full SMTP password is not returned; only the password ID and a partially redacted version are provided.
|
|
552
|
+
* @param handle - Handle of the sub-account to retrieve the SMTP password for.
|
|
553
|
+
* @example
|
|
554
|
+
* ```ts
|
|
555
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
556
|
+
* const { passwords } = await mailchannels.subAccounts.listSmtpPasswords('validhandle123')
|
|
557
|
+
* ```
|
|
558
|
+
*/
|
|
559
|
+
async listSmtpPasswords(handle) {
|
|
560
|
+
const data = { passwords: [], error: null };
|
|
561
|
+
if (!handle) {
|
|
562
|
+
data.error = "No handle provided.";
|
|
563
|
+
return data;
|
|
564
|
+
}
|
|
565
|
+
const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/smtp-password`, {
|
|
566
|
+
onResponseError: async ({ response: response2 }) => {
|
|
567
|
+
data.error = getStatusError(response2, {
|
|
568
|
+
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
}).catch(() => []);
|
|
572
|
+
data.passwords = response.map((password) => ({
|
|
573
|
+
enabled: password.enabled,
|
|
574
|
+
id: password.id,
|
|
575
|
+
value: password.smtp_password
|
|
576
|
+
}));
|
|
577
|
+
return data;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Deletes the SMTP password identified by its ID for the specified sub-account.
|
|
581
|
+
* @param handle - Handle of the sub-account for which the SMTP password should be deleted.
|
|
582
|
+
* @param id - The ID of the SMTP password to delete.
|
|
583
|
+
* @example
|
|
584
|
+
* ```ts
|
|
585
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
586
|
+
* const { success } = await mailchannels.subAccounts.deleteSmtpPassword('validhandle123', 1)
|
|
587
|
+
* ```
|
|
588
|
+
*/
|
|
589
|
+
async deleteSmtpPassword(handle, id) {
|
|
590
|
+
const data = { success: false, error: null };
|
|
591
|
+
if (!handle) {
|
|
592
|
+
data.error = "No handle provided.";
|
|
593
|
+
return data;
|
|
594
|
+
}
|
|
595
|
+
await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/smtp-password/${id}`, {
|
|
596
|
+
ignoreResponseError: true,
|
|
597
|
+
onResponse: async ({ response }) => {
|
|
598
|
+
if (response.ok) {
|
|
599
|
+
data.success = true;
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
data.error = getStatusError(response, {
|
|
603
|
+
[ErrorCode.BadRequest]: "Missing or invalid SMTP password ID."
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
return data;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
class Service {
|
|
612
|
+
constructor(mailchannels) {
|
|
613
|
+
this.mailchannels = mailchannels;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Retrieve the condition of the service
|
|
617
|
+
* @example
|
|
618
|
+
* ```ts
|
|
619
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
620
|
+
* const { success } = await mailchannels.service.status()
|
|
621
|
+
* ```
|
|
622
|
+
*/
|
|
623
|
+
async status() {
|
|
624
|
+
const data = { success: false, error: null };
|
|
625
|
+
await this.mailchannels.get("/inbound/v1/status", {
|
|
626
|
+
ignoreResponseError: true,
|
|
627
|
+
onResponse: async ({ response }) => {
|
|
628
|
+
if (response.ok) {
|
|
629
|
+
data.success = true;
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
data.error = getStatusError(response);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
return data;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Get a list of your subscriptions to MailChannels Inbound
|
|
639
|
+
* @example
|
|
640
|
+
* ```ts
|
|
641
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
642
|
+
* const { subscriptions } = await mailchannels.service.subscriptions()
|
|
643
|
+
* ```
|
|
644
|
+
*/
|
|
645
|
+
async subscriptions() {
|
|
646
|
+
const data = { subscriptions: [], error: null };
|
|
647
|
+
const response = await this.mailchannels.get("/inbound/v1/subscriptions", {
|
|
648
|
+
onResponseError: async ({ response: response2 }) => {
|
|
649
|
+
data.error = getStatusError(response2, {
|
|
650
|
+
[ErrorCode.NotFound]: "We could not find a customer that matched the customerHandle."
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
}).catch(() => []);
|
|
654
|
+
data.subscriptions = response;
|
|
655
|
+
return data;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Submit a false negative or false positive report.
|
|
659
|
+
* @param options - The report options
|
|
660
|
+
*/
|
|
661
|
+
async report(options) {
|
|
662
|
+
const data = { success: false, error: null };
|
|
663
|
+
const { type, ...payload } = options;
|
|
664
|
+
await this.mailchannels.post("/inbound/v1/report", {
|
|
665
|
+
query: {
|
|
666
|
+
report_type: type
|
|
667
|
+
},
|
|
668
|
+
body: payload,
|
|
669
|
+
onResponse: async ({ response }) => {
|
|
670
|
+
if (response.ok) {
|
|
671
|
+
data.success = true;
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
data.error = getStatusError(response);
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
return data;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
class Domains {
|
|
682
|
+
constructor(mailchannels) {
|
|
683
|
+
this.mailchannels = mailchannels;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Provision a single domain to use MailChannels Inbound.
|
|
687
|
+
* @param options - The domain data to provision.
|
|
688
|
+
* @example
|
|
689
|
+
* ```ts
|
|
690
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
691
|
+
* const { data } = await mailchannels.domains.provision({
|
|
692
|
+
* domain: 'example.com',
|
|
693
|
+
* subscriptionHandle: 'your-subscription-handle'
|
|
694
|
+
* })
|
|
695
|
+
* ```
|
|
696
|
+
*/
|
|
697
|
+
async provision(options) {
|
|
698
|
+
const { associateKey, overwrite, ...payload } = options;
|
|
699
|
+
const data = { data: null, error: null };
|
|
700
|
+
const response = await this.mailchannels.post("/inbound/v1/domains", {
|
|
701
|
+
query: {
|
|
702
|
+
"associate-key": associateKey,
|
|
703
|
+
"overwrite": overwrite
|
|
704
|
+
},
|
|
705
|
+
body: payload,
|
|
706
|
+
onResponseError: async ({ response: response2 }) => {
|
|
707
|
+
data.error = getStatusError(response2, {
|
|
708
|
+
[ErrorCode.BadRequest]: "Bad Request, returned in the case that an error occurs while converting an A-label domain to a U-label domain name.",
|
|
709
|
+
[ErrorCode.Forbidden]: "The limit on associated domains is reached or you are attempting to associate a domain with a subscription that is not your own.",
|
|
710
|
+
[ErrorCode.Conflict]: `The domain '${options.domain}' is already provisioned, and is associated with a different customer.`
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
}).catch(() => null);
|
|
714
|
+
data.data = response;
|
|
715
|
+
return data;
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Provision up to 1000 domains to use MailChannels Inbound.
|
|
719
|
+
* @param options - The options to provision the domains.
|
|
720
|
+
* @param domains - The list of domains to provision.
|
|
721
|
+
* @example
|
|
722
|
+
* ```ts
|
|
723
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
724
|
+
* const { results } = await mailchannels.domains.bulkProvision({
|
|
725
|
+
* subscriptionHandle: 'your-subscription-handle'
|
|
726
|
+
* }, [
|
|
727
|
+
* {
|
|
728
|
+
* domain: 'example.com',
|
|
729
|
+
* admins: ['support@example.com']
|
|
730
|
+
* },
|
|
731
|
+
* {
|
|
732
|
+
* domain: 'example2.com'
|
|
733
|
+
* }
|
|
734
|
+
* ])
|
|
735
|
+
* ```
|
|
736
|
+
*/
|
|
737
|
+
async bulkProvision(options, domains) {
|
|
738
|
+
const { associateKey, overwrite, subscriptionHandle } = options;
|
|
739
|
+
const data = { results: null, error: null };
|
|
740
|
+
if (!domains || !domains.length) {
|
|
741
|
+
data.error = "No domains provided.";
|
|
742
|
+
return data;
|
|
743
|
+
}
|
|
744
|
+
if (domains.length > 1e3) {
|
|
745
|
+
data.error = "The maximum number of domains to be provisioned is 1000.";
|
|
746
|
+
return data;
|
|
747
|
+
}
|
|
748
|
+
const response = await this.mailchannels.post("/inbound/v1/domains/batch", {
|
|
749
|
+
query: {
|
|
750
|
+
subscriptionHandle,
|
|
751
|
+
"associate-key": associateKey,
|
|
752
|
+
"overwrite": overwrite
|
|
753
|
+
},
|
|
754
|
+
body: { domains },
|
|
755
|
+
onResponseError: async ({ response: response2 }) => {
|
|
756
|
+
data.error = getStatusError(response2, {
|
|
757
|
+
[ErrorCode.BadRequest]: "Bad Request, returned in the case that a domain name fails RFC 5891 validation.",
|
|
758
|
+
[ErrorCode.Forbidden]: "The limit on associated domains is reached or you are attempting to associate a domain with a subscription that is not your own."
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
}).catch(() => null);
|
|
762
|
+
if (!response) return data;
|
|
763
|
+
data.results = response;
|
|
764
|
+
return data;
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Fetch a list of all domains associated with this API key.
|
|
768
|
+
* @param options - The options to filter the list of domains.
|
|
769
|
+
* @example
|
|
770
|
+
* ```ts
|
|
771
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
772
|
+
* const { domains } = await mailchannels.domains.list()
|
|
773
|
+
* ```
|
|
774
|
+
*/
|
|
775
|
+
async list(options) {
|
|
776
|
+
const data = { domains: [], total: 0, error: null };
|
|
777
|
+
if (typeof options?.limit === "number" && (options.limit < 1 || options.limit > 5e3)) {
|
|
778
|
+
data.error = "The limit value is invalid. Possible limit values are 1 to 5000.";
|
|
779
|
+
return data;
|
|
780
|
+
}
|
|
781
|
+
if (typeof options?.offset === "number" && options.offset < 0) {
|
|
782
|
+
data.error = "Offset must be greater than or equal to 0.";
|
|
783
|
+
return data;
|
|
784
|
+
}
|
|
785
|
+
const response = await this.mailchannels.get("/inbound/v1/domains", {
|
|
786
|
+
query: options,
|
|
787
|
+
onResponseError: async ({ response: response2 }) => {
|
|
788
|
+
data.error = getStatusError(response2);
|
|
789
|
+
}
|
|
790
|
+
}).catch(() => null);
|
|
791
|
+
if (!response) return data;
|
|
792
|
+
data.domains = response.domains;
|
|
793
|
+
data.total = response.total;
|
|
794
|
+
return data;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* De-provision a domain to cease protecting it with MailChannels Inbound.
|
|
798
|
+
* @param domain - The domain name to be removed.
|
|
799
|
+
* @example
|
|
800
|
+
* ```ts
|
|
801
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
802
|
+
* const { success } = await mailchannels.domains.delete('example.com')
|
|
803
|
+
* ```
|
|
804
|
+
*/
|
|
805
|
+
async delete(domain) {
|
|
806
|
+
const data = { success: false, error: null };
|
|
807
|
+
if (!domain) {
|
|
808
|
+
data.error = "No domain provided.";
|
|
809
|
+
return data;
|
|
810
|
+
}
|
|
811
|
+
await this.mailchannels.delete(`/inbound/v1/domains/${domain}`, {
|
|
812
|
+
ignoreResponseError: true,
|
|
813
|
+
onResponse: async ({ response }) => {
|
|
814
|
+
if (response.ok) {
|
|
815
|
+
data.success = true;
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
data.error = getStatusError(response, {
|
|
819
|
+
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, or the domain in the request is an alias domain.",
|
|
820
|
+
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
return data;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Add an entry to a domain blocklist or safelist.
|
|
828
|
+
* @param domain - The domain name.
|
|
829
|
+
* @param options - The options to add a list entry.
|
|
830
|
+
* @example
|
|
831
|
+
* ```ts
|
|
832
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
833
|
+
* const { entry } = await mailchannels.domains.addListEntry('example.com', {
|
|
834
|
+
* listName: 'safelist',
|
|
835
|
+
* item: 'name@domain.com'
|
|
836
|
+
* })
|
|
837
|
+
* ```
|
|
838
|
+
*/
|
|
839
|
+
async addListEntry(domain, options) {
|
|
840
|
+
const { listName, item } = options;
|
|
841
|
+
const data = { entry: null, error: null };
|
|
842
|
+
if (!domain) {
|
|
843
|
+
data.error = "No domain provided.";
|
|
844
|
+
return data;
|
|
845
|
+
}
|
|
846
|
+
if (!listName) {
|
|
847
|
+
data.error = "No list name provided.";
|
|
848
|
+
return data;
|
|
849
|
+
}
|
|
850
|
+
const response = await this.mailchannels.post(`/inbound/v1/domains/${domain}/lists/${listName}`, {
|
|
851
|
+
body: { item },
|
|
852
|
+
onResponseError: async ({ response: response2 }) => {
|
|
853
|
+
data.error = getStatusError(response2, {
|
|
854
|
+
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
855
|
+
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
}).catch(() => null);
|
|
859
|
+
if (!response) return data;
|
|
860
|
+
data.entry = {
|
|
861
|
+
action: response.action,
|
|
862
|
+
item: response.item,
|
|
863
|
+
type: response.item_type
|
|
864
|
+
};
|
|
865
|
+
return data;
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* Get domain list entries.
|
|
869
|
+
* @param domain - The domain name.
|
|
870
|
+
* @param listName - The name of the list to fetch. This can be a `blocklist`, `safelist`, `blacklist`, or `whitelist`.
|
|
871
|
+
* @example
|
|
872
|
+
* ```ts
|
|
873
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
874
|
+
* const { entries } = await mailchannels.domains.listEntries('example.com', 'safelist')
|
|
875
|
+
* ```
|
|
876
|
+
*/
|
|
877
|
+
async listEntries(domain, listName) {
|
|
878
|
+
const data = { entries: [], error: null };
|
|
879
|
+
if (!domain) {
|
|
880
|
+
data.error = "No domain provided.";
|
|
881
|
+
return data;
|
|
882
|
+
}
|
|
883
|
+
if (!listName) {
|
|
884
|
+
data.error = "No list name provided.";
|
|
885
|
+
return data;
|
|
886
|
+
}
|
|
887
|
+
const response = await this.mailchannels.get(`/inbound/v1/domains/${domain}/lists/${listName}`, {
|
|
888
|
+
onResponseError: async ({ response: response2 }) => {
|
|
889
|
+
data.error = getStatusError(response2, {
|
|
890
|
+
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
891
|
+
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
}).catch(() => null);
|
|
895
|
+
if (!response) return data;
|
|
896
|
+
data.entries = response.map(({ action, item, item_type }) => ({
|
|
897
|
+
action,
|
|
898
|
+
item,
|
|
899
|
+
type: item_type
|
|
900
|
+
}));
|
|
901
|
+
return data;
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Delete item from domain list.
|
|
905
|
+
* @param email - The domain name whose list will be modified.
|
|
906
|
+
* @param options - The options for the list entry to delete.
|
|
907
|
+
* @example
|
|
908
|
+
* ```ts
|
|
909
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
910
|
+
* const { success } = await mailchannels.domains.deleteListEntry('example.com', {
|
|
911
|
+
* listName: 'safelist',
|
|
912
|
+
* item: 'name@domain.com'
|
|
913
|
+
* })
|
|
914
|
+
* ```
|
|
915
|
+
*/
|
|
916
|
+
async deleteListEntry(domain, options) {
|
|
917
|
+
const { listName, item } = options;
|
|
918
|
+
const data = { success: false, error: null };
|
|
919
|
+
if (!domain) {
|
|
920
|
+
data.error = "No domain provided.";
|
|
921
|
+
return data;
|
|
922
|
+
}
|
|
923
|
+
if (!listName) {
|
|
924
|
+
data.error = "No list name provided.";
|
|
925
|
+
return data;
|
|
926
|
+
}
|
|
927
|
+
await this.mailchannels.delete(`/inbound/v1/domains/${domain}/lists/${listName}`, {
|
|
928
|
+
query: { item },
|
|
929
|
+
ignoreResponseError: true,
|
|
930
|
+
onResponse: async ({ response }) => {
|
|
931
|
+
if (response.ok) {
|
|
932
|
+
data.success = true;
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
data.error = getStatusError(response, {
|
|
936
|
+
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
937
|
+
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
});
|
|
941
|
+
return data;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Generate a link that allows a user to log in as a domain administrator.
|
|
945
|
+
* @param domain - The domain name.
|
|
946
|
+
* @example
|
|
947
|
+
* ```ts
|
|
948
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
949
|
+
* const { link } = await mailchannels.domains.createLoginLink('example.com')
|
|
950
|
+
* ```
|
|
951
|
+
*/
|
|
952
|
+
async createLoginLink(domain) {
|
|
953
|
+
const data = { link: null, error: null };
|
|
954
|
+
if (!domain) {
|
|
955
|
+
data.error = "No domain provided.";
|
|
956
|
+
return data;
|
|
957
|
+
}
|
|
958
|
+
const response = await this.mailchannels.get(`/inbound/v1/domains/${domain}/login-link`, {
|
|
959
|
+
onResponseError: async ({ response: response2 }) => {
|
|
960
|
+
data.error = getStatusError(response2, {
|
|
961
|
+
[ErrorCode.Unauthorized]: "The domain does not belong to this customer.",
|
|
962
|
+
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
963
|
+
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
}).catch(() => null);
|
|
967
|
+
if (!response) return data;
|
|
968
|
+
data.link = response.loginLink;
|
|
969
|
+
return data;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* Sets the list of downstream addreses for the domain. This action deletes any existing downstream address for the domain before creating new ones. If the `records` parameter is an empty array, all downstream address records will be deleted.
|
|
973
|
+
* @param domain - The domain name.
|
|
974
|
+
* @param records - The list of records to set for the domain. A maximum of 10 records can be set.
|
|
975
|
+
* @example
|
|
976
|
+
* ```ts
|
|
977
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
978
|
+
* const { success } = await mailchannels.domains.setDownstreamAddress('example.com', [
|
|
979
|
+
* {
|
|
980
|
+
* port: 25,
|
|
981
|
+
* priority: 10,
|
|
982
|
+
* target: 'example.com.',
|
|
983
|
+
* weight: 10
|
|
984
|
+
* }
|
|
985
|
+
* ])
|
|
986
|
+
* ```
|
|
987
|
+
*/
|
|
988
|
+
async setDownstreamAddress(domain, records = []) {
|
|
989
|
+
const data = { success: false, error: null };
|
|
990
|
+
if (!domain) {
|
|
991
|
+
data.error = "No domain provided.";
|
|
992
|
+
return data;
|
|
993
|
+
}
|
|
994
|
+
if (records.length > 10) {
|
|
995
|
+
data.error = "The maximum of records to be set is 10.";
|
|
996
|
+
return data;
|
|
997
|
+
}
|
|
998
|
+
await this.mailchannels.put(`/inbound/v1/domains/${domain}/downstream-address`, {
|
|
999
|
+
body: { records },
|
|
1000
|
+
ignoreResponseError: true,
|
|
1001
|
+
onResponse: async ({ response }) => {
|
|
1002
|
+
if (response.ok) {
|
|
1003
|
+
data.success = true;
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
data.error = getStatusError(response, {
|
|
1007
|
+
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
1008
|
+
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
return data;
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Retrieve stored downstream addresses for the domain.
|
|
1016
|
+
* @param domain - The domain name.
|
|
1017
|
+
* @param options - The options to filter the list of downstream addresses.
|
|
1018
|
+
* @example
|
|
1019
|
+
* ```ts
|
|
1020
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
1021
|
+
* const { records } = await mailchannels.domains.listDownstreamAddresses('example.com')
|
|
1022
|
+
* ```
|
|
1023
|
+
*/
|
|
1024
|
+
async listDownstreamAddresses(domain, options) {
|
|
1025
|
+
const data = { records: [], error: null };
|
|
1026
|
+
if (!domain) {
|
|
1027
|
+
data.error = "No domain provided.";
|
|
1028
|
+
return data;
|
|
1029
|
+
}
|
|
1030
|
+
if (typeof options?.limit === "number" && options.limit < 1) {
|
|
1031
|
+
data.error = "The limit value is invalid. Only positive values are allowed.";
|
|
1032
|
+
return data;
|
|
1033
|
+
}
|
|
1034
|
+
if (typeof options?.offset === "number" && options.offset < 0) {
|
|
1035
|
+
data.error = "Offset must be greater than or equal to 0.";
|
|
1036
|
+
return data;
|
|
1037
|
+
}
|
|
1038
|
+
const response = await this.mailchannels.get(`/inbound/v1/domains/${domain}/downstream-address`, {
|
|
1039
|
+
query: options,
|
|
1040
|
+
onResponseError: async ({ response: response2 }) => {
|
|
1041
|
+
data.error = getStatusError(response2, {
|
|
1042
|
+
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
1043
|
+
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
}).catch(() => null);
|
|
1047
|
+
if (!response) return data;
|
|
1048
|
+
data.records = response.records;
|
|
1049
|
+
return data;
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Update the API key that is associated with a domain.
|
|
1053
|
+
* @param domain - The domain name.
|
|
1054
|
+
* @param key - The new API key to associate with this domain.
|
|
1055
|
+
* @example
|
|
1056
|
+
* ```ts
|
|
1057
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
1058
|
+
* const { success } = await mailchannels.domains.updateApiKey('example.com', 'your-api-key')
|
|
1059
|
+
* ```
|
|
1060
|
+
*/
|
|
1061
|
+
async updateApiKey(domain, key) {
|
|
1062
|
+
const data = { success: false, error: null };
|
|
1063
|
+
if (!domain) {
|
|
1064
|
+
data.error = "No domain provided.";
|
|
1065
|
+
return data;
|
|
1066
|
+
}
|
|
1067
|
+
if (!key) {
|
|
1068
|
+
data.error = "No API key provided.";
|
|
1069
|
+
return data;
|
|
1070
|
+
}
|
|
1071
|
+
await this.mailchannels.put(`/inbound/v1/domains/${domain}/api-key`, {
|
|
1072
|
+
body: { apiKey: key },
|
|
1073
|
+
ignoreResponseError: true,
|
|
1074
|
+
onResponse: async ({ response }) => {
|
|
1075
|
+
if (response.ok) {
|
|
1076
|
+
data.success = true;
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
data.error = getStatusError(response, {
|
|
1080
|
+
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
1081
|
+
[ErrorCode.NotFound]: "The domain does not exist."
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
});
|
|
1085
|
+
return data;
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
class Users {
|
|
1090
|
+
constructor(mailchannels) {
|
|
1091
|
+
this.mailchannels = mailchannels;
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Create a recipient user.
|
|
1095
|
+
* @param email - The email address of the user to create.
|
|
1096
|
+
* @param options - The options for the user to create.
|
|
1097
|
+
* @example
|
|
1098
|
+
* ```ts
|
|
1099
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
1100
|
+
* const { user } = await mailchannels.users.create("name@example.com", {
|
|
1101
|
+
* admin: true
|
|
1102
|
+
* })
|
|
1103
|
+
* ```
|
|
1104
|
+
*/
|
|
1105
|
+
async create(email, options) {
|
|
1106
|
+
const { admin, filter, listEntries } = options || {};
|
|
1107
|
+
const data = { user: null, error: null };
|
|
1108
|
+
if (!email) {
|
|
1109
|
+
data.error = "No email address provided.";
|
|
1110
|
+
return data;
|
|
1111
|
+
}
|
|
1112
|
+
const response = await this.mailchannels.put("/inbound/v1/users", {
|
|
1113
|
+
query: {
|
|
1114
|
+
email_address: email,
|
|
1115
|
+
admin: Boolean(admin),
|
|
1116
|
+
filter
|
|
1117
|
+
},
|
|
1118
|
+
body: {
|
|
1119
|
+
list_entries: listEntries
|
|
1120
|
+
},
|
|
1121
|
+
onResponseError: async ({ response: response2 }) => {
|
|
1122
|
+
data.error = getStatusError(response2, {
|
|
1123
|
+
[ErrorCode.BadRequest]: `The email address '${email}' is invalid.`
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
}).catch(() => null);
|
|
1127
|
+
if (!response) return data;
|
|
1128
|
+
data.user = {
|
|
1129
|
+
email: response.recipient.email_address,
|
|
1130
|
+
roles: response.recipient.roles,
|
|
1131
|
+
filter: response.recipient.filter,
|
|
1132
|
+
listEntries: response.list_entries.map(({ item, item_type, action }) => ({
|
|
1133
|
+
item,
|
|
1134
|
+
type: item_type,
|
|
1135
|
+
action
|
|
1136
|
+
}))
|
|
1137
|
+
};
|
|
1138
|
+
return data;
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Add item to recipient user list
|
|
1142
|
+
* @param email - The email address of the recipient whose list will be modified.
|
|
1143
|
+
* @param options - The options for the list entry to add.
|
|
1144
|
+
* @example
|
|
1145
|
+
* ```ts
|
|
1146
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
1147
|
+
* const { entry } = await mailchannels.users.addListEntry('name@example.com', {
|
|
1148
|
+
* listName: 'safelist',
|
|
1149
|
+
* item: 'name@domain.com'
|
|
1150
|
+
* })
|
|
1151
|
+
* ```
|
|
1152
|
+
*/
|
|
1153
|
+
async addListEntry(email, options) {
|
|
1154
|
+
const { listName, item } = options;
|
|
1155
|
+
const data = { entry: null, error: null };
|
|
1156
|
+
if (!email) {
|
|
1157
|
+
data.error = "No email provided.";
|
|
1158
|
+
return data;
|
|
1159
|
+
}
|
|
1160
|
+
if (!listName) {
|
|
1161
|
+
data.error = "No list name provided.";
|
|
1162
|
+
return data;
|
|
1163
|
+
}
|
|
1164
|
+
const response = await this.mailchannels.post(`/inbound/v1/users/${email}/lists/${listName}`, {
|
|
1165
|
+
body: { item },
|
|
1166
|
+
onResponseError: async ({ response: response2 }) => {
|
|
1167
|
+
data.error = getStatusError(response2, {
|
|
1168
|
+
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
1169
|
+
[ErrorCode.NotFound]: `The recipient '${email}' was not found.`
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
}).catch(() => null);
|
|
1173
|
+
if (!response) return data;
|
|
1174
|
+
data.entry = {
|
|
1175
|
+
action: response.action,
|
|
1176
|
+
item: response.item,
|
|
1177
|
+
type: response.item_type
|
|
1178
|
+
};
|
|
1179
|
+
return data;
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Get recipient list entries.
|
|
1183
|
+
* @param email - The email address of the recipient whose list will be fetched.
|
|
1184
|
+
* @param listName - The name of the list to fetch. This can be a `blocklist`, `safelist`, `blacklist`, or `whitelist`.
|
|
1185
|
+
* @example
|
|
1186
|
+
* ```ts
|
|
1187
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
1188
|
+
* const { entries } = await mailchannels.users.listEntries('name@example.com', 'safelist')
|
|
1189
|
+
* ```
|
|
1190
|
+
*/
|
|
1191
|
+
async listEntries(email, listName) {
|
|
1192
|
+
const data = { entries: [], error: null };
|
|
1193
|
+
if (!email) {
|
|
1194
|
+
data.error = "No email provided.";
|
|
1195
|
+
return data;
|
|
1196
|
+
}
|
|
1197
|
+
if (!listName) {
|
|
1198
|
+
data.error = "No list name provided.";
|
|
1199
|
+
return data;
|
|
1200
|
+
}
|
|
1201
|
+
const response = await this.mailchannels.get(`/inbound/v1/users/${email}/lists/${listName}`, {
|
|
1202
|
+
onResponseError: async ({ response: response2 }) => {
|
|
1203
|
+
data.error = getStatusError(response2, {
|
|
1204
|
+
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
1205
|
+
[ErrorCode.NotFound]: `The recipient '${email}' was not found.`
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
}).catch(() => null);
|
|
1209
|
+
if (!response) return data;
|
|
1210
|
+
data.entries = response.map(({ action, item, item_type }) => ({
|
|
1211
|
+
action,
|
|
1212
|
+
item,
|
|
1213
|
+
type: item_type
|
|
1214
|
+
}));
|
|
1215
|
+
return data;
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Delete item from recipient list.
|
|
1219
|
+
* @param email - The email address of the recipient whose list will be modified.
|
|
1220
|
+
* @param options - The options for the list entry to delete.
|
|
1221
|
+
* @example
|
|
1222
|
+
* ```ts
|
|
1223
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
1224
|
+
* const { success } = await mailchannels.users.deleteListEntry('name@example.com', {
|
|
1225
|
+
* listName: 'safelist',
|
|
1226
|
+
* item: 'name@domain.com'
|
|
1227
|
+
* })
|
|
1228
|
+
* ```
|
|
1229
|
+
*/
|
|
1230
|
+
async deleteListEntry(email, options) {
|
|
1231
|
+
const { listName, item } = options;
|
|
1232
|
+
const data = { success: false, error: null };
|
|
1233
|
+
if (!email) {
|
|
1234
|
+
data.error = "No email provided.";
|
|
1235
|
+
return data;
|
|
1236
|
+
}
|
|
1237
|
+
if (!listName) {
|
|
1238
|
+
data.error = "No list name provided.";
|
|
1239
|
+
return data;
|
|
1240
|
+
}
|
|
1241
|
+
await this.mailchannels.delete(`/inbound/v1/users/${email}/lists/${listName}`, {
|
|
1242
|
+
query: { item },
|
|
1243
|
+
ignoreResponseError: true,
|
|
1244
|
+
onResponse: async ({ response }) => {
|
|
1245
|
+
if (response.ok) {
|
|
1246
|
+
data.success = true;
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
data.error = getStatusError(response, {
|
|
1250
|
+
[ErrorCode.Forbidden]: "The domain is associated with an api key that is different than the one in the request, the domain is associated with a different customer, or the domain in the request is an alias domain.",
|
|
1251
|
+
[ErrorCode.NotFound]: `The recipient '${email}' was not found.`
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1255
|
+
return data;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
class Lists {
|
|
1260
|
+
constructor(mailchannels) {
|
|
1261
|
+
this.mailchannels = mailchannels;
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Add item to account-level list
|
|
1265
|
+
* @param options - The options for the list entry to add.
|
|
1266
|
+
* @example
|
|
1267
|
+
* ```ts
|
|
1268
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
1269
|
+
* const { entry } = await mailchannels.lists.addListEntry({
|
|
1270
|
+
* listName: 'safelist',
|
|
1271
|
+
* item: 'name@domain.com'
|
|
1272
|
+
* })
|
|
1273
|
+
* ```
|
|
1274
|
+
*/
|
|
1275
|
+
async addListEntry(options) {
|
|
1276
|
+
const { listName, item } = options;
|
|
1277
|
+
const data = { entry: null, error: null };
|
|
1278
|
+
if (!listName) {
|
|
1279
|
+
data.error = "No list name provided.";
|
|
1280
|
+
return data;
|
|
1281
|
+
}
|
|
1282
|
+
const response = await this.mailchannels.post(`/inbound/v1/lists/${listName}`, {
|
|
1283
|
+
body: { item },
|
|
1284
|
+
onResponseError: async ({ response: response2 }) => {
|
|
1285
|
+
data.error = getStatusError(response2);
|
|
1286
|
+
}
|
|
1287
|
+
}).catch(() => null);
|
|
1288
|
+
if (!response) return data;
|
|
1289
|
+
data.entry = {
|
|
1290
|
+
action: response.action,
|
|
1291
|
+
item: response.item,
|
|
1292
|
+
type: response.item_type
|
|
1293
|
+
};
|
|
1294
|
+
return data;
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Get account-level list entries.
|
|
1298
|
+
* @param listName - The name of the list to fetch. This can be a `blocklist`, `safelist`, `blacklist`, or `whitelist`.
|
|
1299
|
+
* @example
|
|
1300
|
+
* ```ts
|
|
1301
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
1302
|
+
* const { entries } = await mailchannels.lists.listEntries('safelist')
|
|
1303
|
+
* ```
|
|
1304
|
+
*/
|
|
1305
|
+
async listEntries(listName) {
|
|
1306
|
+
const data = { entries: [], error: null };
|
|
1307
|
+
if (!listName) {
|
|
1308
|
+
data.error = "No list name provided.";
|
|
1309
|
+
return data;
|
|
1310
|
+
}
|
|
1311
|
+
const response = await this.mailchannels.get(`/inbound/v1/lists/${listName}`, {
|
|
1312
|
+
onResponseError: async ({ response: response2 }) => {
|
|
1313
|
+
data.error = getStatusError(response2);
|
|
1314
|
+
}
|
|
1315
|
+
}).catch(() => null);
|
|
1316
|
+
if (!response) return data;
|
|
1317
|
+
data.entries = response.map(({ action, item, item_type }) => ({
|
|
1318
|
+
action,
|
|
1319
|
+
item,
|
|
1320
|
+
type: item_type
|
|
1321
|
+
}));
|
|
1322
|
+
return data;
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Delete item from account-level list.
|
|
1326
|
+
* @param options - The options for the list entry to delete.
|
|
1327
|
+
* @example
|
|
1328
|
+
* ```ts
|
|
1329
|
+
* const mailchannels = new MailChannels('your-api-key')
|
|
1330
|
+
* const { success } = await mailchannels.lists.deleteListEntry({
|
|
1331
|
+
* listName: 'safelist',
|
|
1332
|
+
* item: 'name@domain.com'
|
|
1333
|
+
* })
|
|
1334
|
+
* ```
|
|
1335
|
+
*/
|
|
1336
|
+
async deleteListEntry(options) {
|
|
1337
|
+
const { listName, item } = options;
|
|
1338
|
+
const data = { success: false, error: null };
|
|
1339
|
+
if (!listName) {
|
|
1340
|
+
data.error = "No list name provided.";
|
|
1341
|
+
return data;
|
|
1342
|
+
}
|
|
1343
|
+
await this.mailchannels.delete(`/inbound/v1/lists/${listName}`, {
|
|
1344
|
+
query: { item },
|
|
1345
|
+
ignoreResponseError: true,
|
|
1346
|
+
onResponse: async ({ response }) => {
|
|
1347
|
+
if (response.ok) {
|
|
1348
|
+
data.success = true;
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
data.error = getStatusError(response);
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
1354
|
+
return data;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
export { Domains, Emails, Lists, Service, SubAccounts, Users, Webhooks };
|