mailchannels-sdk 0.7.2 → 0.7.4

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.
@@ -1,2330 +1,2058 @@
1
- import { $fetch } from 'ofetch';
2
-
3
- class MailChannelsClient {
4
- static BASE_URL = "https://api.mailchannels.net";
5
- #headers;
6
- constructor(key) {
7
- if (!key) {
8
- throw new Error("Missing MailChannels API key.");
9
- }
10
- this.#headers = {
11
- "X-API-Key": key,
12
- "Accept": "application/json",
13
- "Content-Type": "application/json"
14
- };
15
- }
16
- async _fetch(path, options) {
17
- return $fetch(path, {
18
- baseURL: MailChannelsClient.BASE_URL,
19
- ...options,
20
- headers: {
21
- ...this.#headers,
22
- ...options?.headers
23
- }
24
- });
25
- }
26
- async post(path, options) {
27
- return this._fetch(path, { method: "POST", ...options });
28
- }
29
- async get(path, options) {
30
- return this._fetch(path, { method: "GET", ...options });
31
- }
32
- async delete(path, options) {
33
- return this._fetch(path, { method: "DELETE", ...options });
34
- }
35
- async put(path, options) {
36
- return this._fetch(path, { method: "PUT", ...options });
37
- }
38
- async patch(path, options) {
39
- return this._fetch(path, { method: "PATCH", ...options });
40
- }
41
- }
42
-
43
- var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
44
- ErrorCode2[ErrorCode2["BadRequest"] = 400] = "BadRequest";
45
- ErrorCode2[ErrorCode2["Unauthorized"] = 401] = "Unauthorized";
46
- ErrorCode2[ErrorCode2["Forbidden"] = 403] = "Forbidden";
47
- ErrorCode2[ErrorCode2["NotFound"] = 404] = "NotFound";
48
- ErrorCode2[ErrorCode2["Conflict"] = 409] = "Conflict";
49
- ErrorCode2[ErrorCode2["PayloadTooLarge"] = 413] = "PayloadTooLarge";
50
- ErrorCode2[ErrorCode2["UnprocessableEntity"] = 422] = "UnprocessableEntity";
51
- return ErrorCode2;
52
- })(ErrorCode || {});
1
+ import { $fetch } from "ofetch";
2
+ var MailChannelsClient = class MailChannelsClient {
3
+ static BASE_URL = "https://api.mailchannels.net";
4
+ #headers;
5
+ constructor(key) {
6
+ if (!key) throw new Error("Missing MailChannels API key.");
7
+ this.#headers = {
8
+ "X-API-Key": key,
9
+ "Accept": "application/json",
10
+ "Content-Type": "application/json"
11
+ };
12
+ }
13
+ async _fetch(path, options) {
14
+ return $fetch(path, {
15
+ baseURL: MailChannelsClient.BASE_URL,
16
+ ...options,
17
+ headers: {
18
+ ...this.#headers,
19
+ ...options?.headers
20
+ }
21
+ });
22
+ }
23
+ async post(path, options) {
24
+ return this._fetch(path, {
25
+ method: "POST",
26
+ ...options
27
+ });
28
+ }
29
+ async get(path, options) {
30
+ return this._fetch(path, {
31
+ method: "GET",
32
+ ...options
33
+ });
34
+ }
35
+ async delete(path, options) {
36
+ return this._fetch(path, {
37
+ method: "DELETE",
38
+ ...options
39
+ });
40
+ }
41
+ async put(path, options) {
42
+ return this._fetch(path, {
43
+ method: "PUT",
44
+ ...options
45
+ });
46
+ }
47
+ async patch(path, options) {
48
+ return this._fetch(path, {
49
+ method: "PATCH",
50
+ ...options
51
+ });
52
+ }
53
+ };
54
+ let ErrorCode = /* @__PURE__ */ function(ErrorCode) {
55
+ ErrorCode[ErrorCode["BadRequest"] = 400] = "BadRequest";
56
+ ErrorCode[ErrorCode["Unauthorized"] = 401] = "Unauthorized";
57
+ ErrorCode[ErrorCode["Forbidden"] = 403] = "Forbidden";
58
+ ErrorCode[ErrorCode["NotFound"] = 404] = "NotFound";
59
+ ErrorCode[ErrorCode["Conflict"] = 409] = "Conflict";
60
+ ErrorCode[ErrorCode["PayloadTooLarge"] = 413] = "PayloadTooLarge";
61
+ ErrorCode[ErrorCode["UnprocessableEntity"] = 422] = "UnprocessableEntity";
62
+ return ErrorCode;
63
+ }({});
53
64
  const createError = (message, statusCode = null) => {
54
- return {
55
- message,
56
- statusCode
57
- };
65
+ return {
66
+ message,
67
+ statusCode
68
+ };
58
69
  };
59
70
  const getStatusError = (response, errors = {}) => {
60
- const statusText = errors[response.status] || "Unknown error.";
61
- const payload = response._data ?? response.data;
62
- let details;
63
- if (typeof payload === "string") {
64
- details = payload;
65
- } else if (payload?.message) {
66
- details = payload.message;
67
- } else if (Array.isArray(payload?.errors) && payload.errors?.length) {
68
- details = payload.errors.join(", ");
69
- }
70
- return createError(details ? `${statusText} ${details}` : statusText, response.status ?? null);
71
+ const statusText = errors[response.status] || "Unknown error.";
72
+ const payload = response._data ?? response.data;
73
+ let details;
74
+ if (typeof payload === "string") details = payload;
75
+ else if (payload?.message) details = payload.message;
76
+ else if (Array.isArray(payload?.errors) && payload.errors?.length) details = payload.errors.join(", ");
77
+ return createError(details ? `${statusText} ${details}` : statusText, response.status ?? null);
71
78
  };
72
79
  const getResultError = (e, fallback) => {
73
- return createError(e instanceof Error ? e.message : fallback);
80
+ return createError(e instanceof Error ? e.message : fallback);
81
+ };
82
+ const validatePagination = (pagination = {}) => {
83
+ const { limit, offset, max } = pagination;
84
+ if (typeof limit === "number" && (limit < 1 || max && limit > max)) return createError("The limit value " + (max ? `must be between 1 and ${max}.` : "is invalid. Only positive values are allowed."));
85
+ if (typeof offset === "number" && offset < 0) return createError("Offset must be greater than or equal to 0.");
86
+ return null;
74
87
  };
75
-
76
88
  const isValidEmail = (email) => {
77
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
89
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
78
90
  };
79
91
  const parseRecipientString = (input) => {
80
- const trimmed = input.trim();
81
- const match = trimmed.match(/^([^<]*)<([^>]*)>$/);
82
- if (match) {
83
- const [, name, email] = match;
84
- if (!email?.trim() || !isValidEmail(email.trim())) return void 0;
85
- return { email: email.trim(), name: name?.trim() };
86
- }
87
- if (!isValidEmail(trimmed)) return void 0;
88
- return { email: trimmed };
92
+ const trimmed = input.trim();
93
+ const match = trimmed.match(/^([^<]*)<([^>]*)>$/);
94
+ if (match) {
95
+ const [, name, email] = match;
96
+ if (!email?.trim() || !isValidEmail(email.trim())) return void 0;
97
+ return {
98
+ email: email.trim(),
99
+ name: name?.trim()
100
+ };
101
+ }
102
+ if (!isValidEmail(trimmed)) return void 0;
103
+ return { email: trimmed };
89
104
  };
90
105
  const parseRecipient = (recipient) => {
91
- if (typeof recipient === "string") {
92
- return parseRecipientString(recipient);
93
- }
94
- if (!recipient?.email || !isValidEmail(recipient.email)) return void 0;
95
- return { email: recipient.email, name: recipient.name };
106
+ if (typeof recipient === "string") return parseRecipientString(recipient);
107
+ if (!recipient?.email || !isValidEmail(recipient.email)) return void 0;
108
+ return {
109
+ email: recipient.email,
110
+ name: recipient.name
111
+ };
96
112
  };
97
113
  const parseArrayRecipients = (recipients) => {
98
- if (!recipients) return void 0;
99
- const arr = typeof recipients === "string" ? [parseRecipientString(recipients)] : Array.isArray(recipients) ? recipients.map(parseRecipient) : [recipients];
100
- const filtered = arr.filter((recipient) => Boolean(recipient));
101
- return filtered.length > 0 ? filtered : void 0;
114
+ if (!recipients) return void 0;
115
+ const filtered = (typeof recipients === "string" ? [parseRecipientString(recipients)] : Array.isArray(recipients) ? recipients.map(parseRecipient) : [recipients]).filter((recipient) => Boolean(recipient));
116
+ return filtered.length > 0 ? filtered : void 0;
102
117
  };
103
-
104
118
  const stripPemHeaders = (pem) => pem.replace(/-----[^-]+-----|\s|#.*$/gm, "");
105
119
  const clean = (data) => {
106
- if (Array.isArray(data)) {
107
- const result = [];
108
- for (let i = 0; i < data.length; i++) {
109
- const cleaned = clean(data[i]);
110
- if (cleaned !== void 0) {
111
- result.push(cleaned);
112
- }
113
- }
114
- return result;
115
- }
116
- if (data && typeof data === "object" && data.constructor === Object) {
117
- const result = {};
118
- const obj = data;
119
- const keys = Object.keys(obj);
120
- for (let i = 0; i < keys.length; i++) {
121
- const key = keys[i];
122
- const cleaned = clean(obj[key]);
123
- if (cleaned !== void 0) {
124
- result[key] = cleaned;
125
- }
126
- }
127
- return result;
128
- }
129
- return data;
120
+ if (Array.isArray(data)) {
121
+ const result = [];
122
+ for (let i = 0; i < data.length; i++) {
123
+ const cleaned = clean(data[i]);
124
+ if (cleaned !== void 0) result.push(cleaned);
125
+ }
126
+ return result;
127
+ }
128
+ if (data && typeof data === "object" && data.constructor === Object) {
129
+ const result = {};
130
+ const obj = data;
131
+ const keys = Object.keys(obj);
132
+ for (let i = 0; i < keys.length; i++) {
133
+ const key = keys[i];
134
+ const cleaned = clean(obj[key]);
135
+ if (cleaned !== void 0) result[key] = cleaned;
136
+ }
137
+ return result;
138
+ }
139
+ return data;
140
+ };
141
+ const mapBuckets = (arr) => {
142
+ return arr.map(({ count, period_start }) => ({
143
+ count,
144
+ periodStart: period_start
145
+ }));
130
146
  };
131
- const validateLimit = (limit, max) => {
132
- if (typeof limit === "number" && (limit < 1 || max && limit > max)) {
133
- return createError("The limit value " + (max ? `must be between 1 and ${max}.` : "is invalid. Only positive values are allowed."));
134
- }
135
- return null;
147
+ var Emails = class {
148
+ constructor(mailchannels) {
149
+ this.mailchannels = mailchannels;
150
+ }
151
+ async _sendEmail(options, flags) {
152
+ let error = null;
153
+ const { cc, bcc, from, to, html, text, mustaches, dkim } = options;
154
+ const parsedFrom = parseRecipient(from);
155
+ if (!parsedFrom || !parsedFrom.email) {
156
+ error = createError("No sender provided. Use the `from` option to specify a sender");
157
+ return {
158
+ success: false,
159
+ data: null,
160
+ error
161
+ };
162
+ }
163
+ const parsedTo = parseArrayRecipients(to);
164
+ if (!parsedTo || !parsedTo.length) {
165
+ error = createError("No recipients provided. Use the `to` option to specify at least one recipient");
166
+ return {
167
+ success: false,
168
+ data: null,
169
+ error
170
+ };
171
+ }
172
+ if (!text && !html) {
173
+ error = createError("No email content provided");
174
+ return {
175
+ success: false,
176
+ data: null,
177
+ error
178
+ };
179
+ }
180
+ const content = [];
181
+ const template_type = mustaches ? "mustache" : void 0;
182
+ if (text) content.push({
183
+ type: "text/plain",
184
+ value: text,
185
+ template_type
186
+ });
187
+ if (html) content.push({
188
+ type: "text/html",
189
+ value: html,
190
+ template_type
191
+ });
192
+ const payload = {
193
+ attachments: options.attachments,
194
+ campaign_id: options.campaignId,
195
+ personalizations: [{
196
+ bcc: parseArrayRecipients(bcc),
197
+ cc: parseArrayRecipients(cc),
198
+ to: parsedTo,
199
+ dkim_domain: dkim?.domain || void 0,
200
+ dkim_private_key: dkim?.privateKey ? stripPemHeaders(dkim.privateKey) : void 0,
201
+ dkim_selector: dkim?.selector || void 0,
202
+ dynamic_template_data: options.mustaches
203
+ }],
204
+ headers: options.headers,
205
+ reply_to: parseRecipient(options.replyTo),
206
+ envelope_from: parseRecipient(options.envelopeFrom),
207
+ from: parsedFrom,
208
+ subject: options.subject,
209
+ content,
210
+ tracking_settings: options.tracking ? {
211
+ click_tracking: options.tracking.click ? { enable: options.tracking.click } : void 0,
212
+ open_tracking: options.tracking.open ? { enable: options.tracking.open } : void 0
213
+ } : void 0,
214
+ transactional: options.transactional
215
+ };
216
+ const endpoint = flags.async ? "/tx/v1/send-async" : "/tx/v1/send";
217
+ const response = await this.mailchannels.post(endpoint, {
218
+ query: { "dry-run": flags.dryRun },
219
+ body: payload,
220
+ onResponseError: async ({ response }) => {
221
+ error = getStatusError(response, {
222
+ [ErrorCode.BadRequest]: "Bad Request.",
223
+ [ErrorCode.Forbidden]: "User does not have access to this feature.",
224
+ [ErrorCode.PayloadTooLarge]: "The total message size should not exceed 30MB. This includes the message itself, headers, and the combined size of any attachments."
225
+ });
226
+ }
227
+ }).catch((e) => {
228
+ error ||= getResultError(e, flags.async ? "Failed to queue email." : "Failed to send email.");
229
+ return null;
230
+ });
231
+ if (!response) return {
232
+ success: false,
233
+ data: null,
234
+ error
235
+ };
236
+ if (flags.async) {
237
+ const asyncResponse = response;
238
+ return {
239
+ data: clean({
240
+ queuedAt: asyncResponse.queued_at,
241
+ requestId: asyncResponse.request_id
242
+ }),
243
+ error: null
244
+ };
245
+ }
246
+ const syncResponse = response;
247
+ const data = clean({
248
+ rendered: syncResponse.data,
249
+ requestId: syncResponse.request_id,
250
+ results: syncResponse.results?.map((result) => ({
251
+ index: result.index,
252
+ messageId: result.message_id,
253
+ reason: result.reason,
254
+ status: result.status
255
+ }))
256
+ });
257
+ return {
258
+ success: !!data,
259
+ data,
260
+ error: null
261
+ };
262
+ }
263
+ async send(options, dryRun = false) {
264
+ return this._sendEmail(options, { dryRun });
265
+ }
266
+ async sendAsync(options) {
267
+ return this._sendEmail(options, { async: true });
268
+ }
269
+ async checkDomain(options) {
270
+ let error = null;
271
+ const { dkim, domain, senderId } = options;
272
+ const payload = {
273
+ dkim_settings: (dkim ? Array.isArray(dkim) ? dkim : [dkim] : void 0)?.map(({ domain, privateKey, selector }) => ({
274
+ dkim_domain: domain,
275
+ dkim_private_key: privateKey ? stripPemHeaders(privateKey) : void 0,
276
+ dkim_selector: selector
277
+ })),
278
+ domain,
279
+ sender_id: senderId
280
+ };
281
+ const response = await this.mailchannels.post("/tx/v1/check-domain", {
282
+ body: payload,
283
+ onResponseError: async ({ response }) => {
284
+ error = getStatusError(response, {
285
+ [ErrorCode.BadRequest]: "Bad Request.",
286
+ [ErrorCode.Forbidden]: "User does not have access to this feature."
287
+ });
288
+ }
289
+ }).catch((e) => {
290
+ error ||= getResultError(e, "Failed to check domain.");
291
+ return null;
292
+ });
293
+ if (!response) return {
294
+ data: null,
295
+ error
296
+ };
297
+ return {
298
+ data: clean({
299
+ dkim: response.check_results.dkim.map((dkimResults) => ({
300
+ domain: dkimResults.dkim_domain,
301
+ keyStatus: dkimResults.dkim_key_status,
302
+ selector: dkimResults.dkim_selector,
303
+ reason: dkimResults.reason,
304
+ verdict: dkimResults.verdict
305
+ })),
306
+ domainLockdown: response.check_results.domain_lockdown,
307
+ senderDomain: response.check_results.sender_domain,
308
+ spf: response.check_results.spf,
309
+ references: response.references
310
+ }),
311
+ error: null
312
+ };
313
+ }
314
+ async createDkimKey(domain, options) {
315
+ let error = null;
316
+ if (!options.selector || options.selector.length > 63) {
317
+ error = createError("Selector must be between 1 and 63 characters.");
318
+ return {
319
+ data: null,
320
+ error
321
+ };
322
+ }
323
+ const payload = {
324
+ algorithm: options.algorithm,
325
+ key_length: options.length,
326
+ selector: options.selector
327
+ };
328
+ const response = await this.mailchannels.post(`/tx/v1/domains/${domain}/dkim-keys`, {
329
+ body: payload,
330
+ onResponseError: async ({ response }) => {
331
+ error = getStatusError(response, {
332
+ [ErrorCode.BadRequest]: "Bad Request.",
333
+ [ErrorCode.Conflict]: "Key pair already created for domain, and selector."
334
+ });
335
+ }
336
+ }).catch((e) => {
337
+ error ||= getResultError(e, "Failed to create DKIM key.");
338
+ return null;
339
+ });
340
+ if (!response) return {
341
+ data: null,
342
+ error
343
+ };
344
+ return {
345
+ data: clean({
346
+ algorithm: response.algorithm,
347
+ createdAt: response.created_at,
348
+ dnsRecords: response.dkim_dns_records,
349
+ domain: response.domain,
350
+ gracePeriodExpiresAt: response.gracePeriodExpiresAt,
351
+ length: response.key_length,
352
+ publicKey: response.public_key,
353
+ retiresAt: response.retiresAt,
354
+ selector: response.selector,
355
+ status: response.status,
356
+ statusModifiedAt: response.status_modified_at
357
+ }),
358
+ error: null
359
+ };
360
+ }
361
+ async getDkimKeys(domain, options) {
362
+ let error = null;
363
+ if (options?.selector && options.selector.length > 63) {
364
+ error = createError("Selector must be between 1 and 63 characters.");
365
+ return {
366
+ data: null,
367
+ error
368
+ };
369
+ }
370
+ error = validatePagination({
371
+ ...options,
372
+ max: 100
373
+ });
374
+ if (error) return {
375
+ data: null,
376
+ error
377
+ };
378
+ const payload = {
379
+ selector: options?.selector,
380
+ status: options?.status,
381
+ offset: options?.offset,
382
+ limit: options?.limit,
383
+ include_dns_record: options?.includeDnsRecord
384
+ };
385
+ const response = await this.mailchannels.get(`/tx/v1/domains/${domain}/dkim-keys`, {
386
+ query: payload,
387
+ onResponseError: async ({ response }) => {
388
+ error = getStatusError(response, { [ErrorCode.BadRequest]: "Bad Request." });
389
+ }
390
+ }).catch((e) => {
391
+ error ||= getResultError(e, "Failed to fetch DKIM keys.");
392
+ return null;
393
+ });
394
+ if (!response) return {
395
+ data: null,
396
+ error
397
+ };
398
+ return {
399
+ data: clean(response.keys.map((key) => ({
400
+ algorithm: key.algorithm,
401
+ createdAt: key.created_at,
402
+ dnsRecords: key.dkim_dns_records,
403
+ domain: key.domain,
404
+ gracePeriodExpiresAt: key.gracePeriodExpiresAt,
405
+ length: key.key_length,
406
+ publicKey: key.public_key,
407
+ retiresAt: key.retiresAt,
408
+ selector: key.selector,
409
+ status: key.status,
410
+ statusModifiedAt: key.status_modified_at
411
+ }))),
412
+ error: null
413
+ };
414
+ }
415
+ async updateDkimKey(domain, options) {
416
+ let error = null;
417
+ if (!options.selector || options.selector.length > 63) {
418
+ error = createError("Selector must be between 1 and 63 characters.");
419
+ return {
420
+ success: false,
421
+ error
422
+ };
423
+ }
424
+ const payload = { status: options.status };
425
+ await this.mailchannels.patch(`/tx/v1/domains/${domain}/dkim-keys/${options.selector}`, {
426
+ body: payload,
427
+ onResponseError: async ({ response }) => {
428
+ error = getStatusError(response, {
429
+ [ErrorCode.BadRequest]: "Bad Request.",
430
+ [ErrorCode.NotFound]: "Specified key pair not found, or no active key for rotation. This may also occur if the DKIM domain or selector path parameter is missing."
431
+ });
432
+ }
433
+ }).catch((e) => {
434
+ error ||= getResultError(e, "Failed to update DKIM key.");
435
+ });
436
+ return {
437
+ success: !error,
438
+ error
439
+ };
440
+ }
441
+ async rotateDkimKey(domain, selector, options) {
442
+ let error = null;
443
+ if (!selector || selector.length > 63) {
444
+ error = createError("Selector must be between 1 and 63 characters.");
445
+ return {
446
+ data: null,
447
+ error
448
+ };
449
+ }
450
+ if (!options.newKey.selector || options.newKey.selector.length > 63) {
451
+ error = createError("New key selector must be between 1 and 63 characters.");
452
+ return {
453
+ data: null,
454
+ error
455
+ };
456
+ }
457
+ const payload = { new_key: { selector: options.newKey.selector } };
458
+ const response = await this.mailchannels.post(`/tx/v1/domains/${domain}/dkim-keys/${selector}/rotate`, {
459
+ body: payload,
460
+ onResponseError: async ({ response }) => {
461
+ error = getStatusError(response, {
462
+ [ErrorCode.BadRequest]: "Bad Request.",
463
+ [ErrorCode.NotFound]: "Specified key pair not found.",
464
+ [ErrorCode.Conflict]: "Key pair already created for domain, and provided new key selector."
465
+ });
466
+ }
467
+ }).catch((e) => {
468
+ error ||= getResultError(e, "Failed to rotate DKIM key.");
469
+ return null;
470
+ });
471
+ if (!response) return {
472
+ data: null,
473
+ error
474
+ };
475
+ return {
476
+ data: clean({
477
+ new: {
478
+ algorithm: response.new_key.algorithm,
479
+ createdAt: response.new_key.created_at,
480
+ dnsRecords: response.new_key.dkim_dns_records,
481
+ domain: response.new_key.domain,
482
+ gracePeriodExpiresAt: response.new_key.gracePeriodExpiresAt,
483
+ length: response.new_key.key_length,
484
+ publicKey: response.new_key.public_key,
485
+ retiresAt: response.new_key.retiresAt,
486
+ selector: response.new_key.selector,
487
+ status: response.new_key.status,
488
+ statusModifiedAt: response.new_key.status_modified_at
489
+ },
490
+ rotated: {
491
+ algorithm: response.rotated_key.algorithm,
492
+ createdAt: response.rotated_key.created_at,
493
+ dnsRecords: response.rotated_key.dkim_dns_records,
494
+ domain: response.rotated_key.domain,
495
+ gracePeriodExpiresAt: response.rotated_key.gracePeriodExpiresAt,
496
+ length: response.rotated_key.key_length,
497
+ publicKey: response.rotated_key.public_key,
498
+ retiresAt: response.rotated_key.retiresAt,
499
+ selector: response.rotated_key.selector,
500
+ status: response.rotated_key.status,
501
+ statusModifiedAt: response.rotated_key.status_modified_at
502
+ }
503
+ }),
504
+ error: null
505
+ };
506
+ }
136
507
  };
137
- const validateOffset = (offset) => {
138
- if (typeof offset === "number" && offset < 0) {
139
- return createError("Offset must be greater than or equal to 0.");
140
- }
141
- return null;
508
+ var Webhooks = class {
509
+ constructor(mailchannels) {
510
+ this.mailchannels = mailchannels;
511
+ }
512
+ async enroll(endpoint) {
513
+ let error = null;
514
+ if (!endpoint) {
515
+ error = createError("No endpoint provided.");
516
+ return {
517
+ success: false,
518
+ error
519
+ };
520
+ }
521
+ if (endpoint.length > 8e3) {
522
+ error = createError("The endpoint exceeds the maximum length of 8000 characters.");
523
+ return {
524
+ success: false,
525
+ error
526
+ };
527
+ }
528
+ await this.mailchannels.post("/tx/v1/webhook", {
529
+ query: { endpoint },
530
+ onResponseError: async ({ response }) => {
531
+ error = getStatusError(response, { [ErrorCode.Conflict]: `Endpoint '${endpoint}' is already enrolled to receive notifications.` });
532
+ }
533
+ }).catch((e) => {
534
+ error ||= getResultError(e, "Failed to enroll webhook.");
535
+ });
536
+ return {
537
+ success: !error,
538
+ error
539
+ };
540
+ }
541
+ async list() {
542
+ let error = null;
543
+ const response = await this.mailchannels.get("/tx/v1/webhook", { onResponseError: async ({ response }) => {
544
+ error = getStatusError(response);
545
+ } }).catch((e) => {
546
+ error ||= getResultError(e, "Failed to fetch webhooks.");
547
+ return null;
548
+ });
549
+ if (!response) return {
550
+ data: null,
551
+ error
552
+ };
553
+ return {
554
+ data: clean(response.map(({ webhook }) => webhook)),
555
+ error: null
556
+ };
557
+ }
558
+ async delete() {
559
+ let error = null;
560
+ await this.mailchannels.delete("/tx/v1/webhook", { onResponseError: async ({ response }) => {
561
+ error = getStatusError(response);
562
+ } }).catch((e) => {
563
+ error ||= getResultError(e, "Failed to delete webhooks.");
564
+ });
565
+ return {
566
+ success: !error,
567
+ error
568
+ };
569
+ }
570
+ async getSigningKey(id) {
571
+ let error = null;
572
+ const response = await this.mailchannels.get("/tx/v1/webhook/public-key", {
573
+ query: { id },
574
+ onResponseError: async ({ response }) => {
575
+ error = getStatusError(response, {
576
+ [ErrorCode.BadRequest]: "Bad Request.",
577
+ [ErrorCode.NotFound]: `The key '${id}' is not found.`
578
+ });
579
+ }
580
+ }).catch((e) => {
581
+ error ||= getResultError(e, "Failed to get signing key.");
582
+ return null;
583
+ });
584
+ if (!response) return {
585
+ data: null,
586
+ error
587
+ };
588
+ return {
589
+ data: clean({ key: response.key }),
590
+ error: null
591
+ };
592
+ }
593
+ async validate(requestId) {
594
+ let error = null;
595
+ if (requestId && requestId.length > 28) {
596
+ error = createError("The request id should not exceed 28 characters.");
597
+ return {
598
+ data: null,
599
+ error
600
+ };
601
+ }
602
+ const response = await this.mailchannels.post("/tx/v1/webhook/validate", {
603
+ body: { request_id: requestId },
604
+ onResponseError: async ({ response }) => {
605
+ error = getStatusError(response, {
606
+ [ErrorCode.BadRequest]: "Bad Request.",
607
+ [ErrorCode.NotFound]: "No webhooks found for the account."
608
+ });
609
+ }
610
+ }).catch((e) => {
611
+ error ||= getResultError(e, "Failed to validate webhooks.");
612
+ return null;
613
+ });
614
+ if (!response) return {
615
+ data: null,
616
+ error
617
+ };
618
+ return {
619
+ data: clean({
620
+ allPassed: response.all_passed,
621
+ results: response.results
622
+ }),
623
+ error: null
624
+ };
625
+ }
142
626
  };
143
- const mapBuckets = (arr) => {
144
- return arr.map(({ count, period_start }) => ({ count, periodStart: period_start }));
627
+ var SubAccounts = class SubAccounts {
628
+ static COMPANY_PATTERN = /^.{3,128}$/;
629
+ static HANDLE_PATTERN = /^[a-z0-9]{3,128}$/;
630
+ constructor(mailchannels) {
631
+ this.mailchannels = mailchannels;
632
+ }
633
+ async create(companyName, handle) {
634
+ let error = null;
635
+ if (!SubAccounts.COMPANY_PATTERN.test(companyName)) {
636
+ error = createError("Invalid company name. Company name must be between 3 and 128 characters.");
637
+ return {
638
+ data: null,
639
+ error
640
+ };
641
+ }
642
+ if (handle) {
643
+ if (!SubAccounts.HANDLE_PATTERN.test(handle)) {
644
+ error = createError("Invalid handle. Sub-account handle must be between 3 and 128 characters and contain only lowercase letters and numbers.");
645
+ return {
646
+ data: null,
647
+ error
648
+ };
649
+ }
650
+ }
651
+ const response = await this.mailchannels.post("/tx/v1/sub-account", {
652
+ body: {
653
+ company_name: companyName,
654
+ handle
655
+ },
656
+ onResponseError: async ({ response }) => {
657
+ error = getStatusError(response, {
658
+ [ErrorCode.Forbidden]: "The parent account does not have permission to create sub-accounts.",
659
+ [ErrorCode.Conflict]: `Sub-account with handle '${handle}' already exists.`
660
+ });
661
+ }
662
+ }).catch((e) => {
663
+ error ||= getResultError(e, "Failed to create sub-account.");
664
+ return null;
665
+ });
666
+ if (!response) return {
667
+ data: null,
668
+ error
669
+ };
670
+ return {
671
+ data: clean({
672
+ companyName: response.company_name,
673
+ enabled: response.enabled,
674
+ handle: response.handle
675
+ }),
676
+ error: null
677
+ };
678
+ }
679
+ async list(options) {
680
+ let error = null;
681
+ error = validatePagination({
682
+ ...options,
683
+ max: 1e3
684
+ });
685
+ if (error) return {
686
+ data: null,
687
+ error
688
+ };
689
+ const response = await this.mailchannels.get("/tx/v1/sub-account", {
690
+ query: options,
691
+ onResponseError: async ({ response }) => {
692
+ error = getStatusError(response);
693
+ }
694
+ }).catch((e) => {
695
+ error ||= getResultError(e, "Failed to fetch sub-accounts.");
696
+ return null;
697
+ });
698
+ if (!response) return {
699
+ data: null,
700
+ error
701
+ };
702
+ return {
703
+ data: clean(response.map((account) => ({
704
+ companyName: account.company_name,
705
+ enabled: account.enabled,
706
+ handle: account.handle
707
+ }))),
708
+ error: null
709
+ };
710
+ }
711
+ async delete(handle) {
712
+ let error = null;
713
+ if (!handle) {
714
+ error = createError("No handle provided.");
715
+ return {
716
+ success: false,
717
+ error
718
+ };
719
+ }
720
+ await this.mailchannels.delete(`/tx/v1/sub-account/${handle}`, { onResponseError: async ({ response }) => {
721
+ error = getStatusError(response);
722
+ } }).catch((e) => {
723
+ error ||= getResultError(e, "Failed to delete sub-account.");
724
+ });
725
+ return {
726
+ success: !error,
727
+ error
728
+ };
729
+ }
730
+ async suspend(handle) {
731
+ let error = null;
732
+ if (!handle) {
733
+ error = createError("No handle provided.");
734
+ return {
735
+ success: false,
736
+ error
737
+ };
738
+ }
739
+ await this.mailchannels.post(`/tx/v1/sub-account/${handle}/suspend`, { onResponseError: async ({ response }) => {
740
+ error = getStatusError(response, { [ErrorCode.NotFound]: `The specified sub-account '${handle}' does not exist.` });
741
+ } }).catch((e) => {
742
+ error ||= getResultError(e, "Failed to suspend sub-account.");
743
+ });
744
+ return {
745
+ success: !error,
746
+ error
747
+ };
748
+ }
749
+ async activate(handle) {
750
+ let error = null;
751
+ if (!handle) {
752
+ error = createError("No handle provided.");
753
+ return {
754
+ success: false,
755
+ error
756
+ };
757
+ }
758
+ await this.mailchannels.post(`/tx/v1/sub-account/${handle}/activate`, { onResponseError: async ({ response }) => {
759
+ error = getStatusError(response, {
760
+ [ErrorCode.Forbidden]: "The parent account does not have permission to activate the sub-account.",
761
+ [ErrorCode.NotFound]: `The specified sub-account '${handle}' does not exist.`
762
+ });
763
+ } }).catch((e) => {
764
+ error ||= getResultError(e, "Failed to activate sub-account.");
765
+ });
766
+ return {
767
+ success: !error,
768
+ error
769
+ };
770
+ }
771
+ async createApiKey(handle) {
772
+ let error = null;
773
+ if (!handle) {
774
+ error = createError("No handle provided.");
775
+ return {
776
+ data: null,
777
+ error
778
+ };
779
+ }
780
+ const response = await this.mailchannels.post(`/tx/v1/sub-account/${handle}/api-key`, { onResponseError: async ({ response }) => {
781
+ error = getStatusError(response, {
782
+ [ErrorCode.Forbidden]: "You can't create API keys for this sub-account.",
783
+ [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`,
784
+ [ErrorCode.UnprocessableEntity]: "You have reached the limit of API keys you can create for this sub-account."
785
+ });
786
+ } }).catch((e) => {
787
+ error ||= getResultError(e, "Failed to create sub-account API key.");
788
+ return null;
789
+ });
790
+ if (!response) return {
791
+ data: null,
792
+ error
793
+ };
794
+ return {
795
+ data: clean({
796
+ id: response.id,
797
+ value: response.key
798
+ }),
799
+ error: null
800
+ };
801
+ }
802
+ async listApiKeys(handle, options) {
803
+ let error = null;
804
+ if (!handle) {
805
+ error = createError("No handle provided.");
806
+ return {
807
+ data: null,
808
+ error
809
+ };
810
+ }
811
+ error = validatePagination({
812
+ ...options,
813
+ max: 1e3
814
+ });
815
+ if (error) return {
816
+ data: null,
817
+ error
818
+ };
819
+ const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/api-key`, { onResponseError: async ({ response }) => {
820
+ error = getStatusError(response, { [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.` });
821
+ } }).catch((e) => {
822
+ error ||= getResultError(e, "Failed to fetch sub-account API keys.");
823
+ return null;
824
+ });
825
+ if (!response) return {
826
+ data: null,
827
+ error
828
+ };
829
+ return {
830
+ data: clean(response.map((key) => ({
831
+ id: key.id,
832
+ value: key.key
833
+ }))),
834
+ error: null
835
+ };
836
+ }
837
+ async deleteApiKey(handle, id) {
838
+ let error = null;
839
+ if (!handle) {
840
+ error = createError("No handle provided.");
841
+ return {
842
+ success: false,
843
+ error
844
+ };
845
+ }
846
+ await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/api-key/${id}`, { onResponseError: async ({ response }) => {
847
+ error = getStatusError(response, { [ErrorCode.BadRequest]: "Missing or invalid API key ID." });
848
+ } }).catch((e) => {
849
+ error ||= getResultError(e, "Failed to delete sub-account API key.");
850
+ });
851
+ return {
852
+ success: !error,
853
+ error
854
+ };
855
+ }
856
+ async createSmtpPassword(handle) {
857
+ let error = null;
858
+ if (!handle) {
859
+ error = createError("No handle provided.");
860
+ return {
861
+ data: null,
862
+ error
863
+ };
864
+ }
865
+ const response = await this.mailchannels.post(`/tx/v1/sub-account/${handle}/smtp-password`, { onResponseError: async ({ response }) => {
866
+ error = getStatusError(response, {
867
+ [ErrorCode.Forbidden]: "You can't create SMTP passwords for this sub-account.",
868
+ [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`,
869
+ [ErrorCode.UnprocessableEntity]: "You have reached the limit of SMTP passwords you can create for this sub-account."
870
+ });
871
+ } }).catch((e) => {
872
+ error ||= getResultError(e, "Failed to create sub-account SMTP password.");
873
+ return null;
874
+ });
875
+ if (!response) return {
876
+ data: null,
877
+ error
878
+ };
879
+ return {
880
+ data: clean({
881
+ enabled: response.enabled,
882
+ id: response.id,
883
+ value: response.smtp_password
884
+ }),
885
+ error: null
886
+ };
887
+ }
888
+ async listSmtpPasswords(handle) {
889
+ let error = null;
890
+ if (!handle) {
891
+ error = createError("No handle provided.");
892
+ return {
893
+ data: null,
894
+ error
895
+ };
896
+ }
897
+ const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/smtp-password`, { onResponseError: async ({ response }) => {
898
+ error = getStatusError(response, { [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.` });
899
+ } }).catch((e) => {
900
+ error ||= getResultError(e, "Failed to fetch sub-account SMTP passwords.");
901
+ return null;
902
+ });
903
+ if (!response) return {
904
+ data: null,
905
+ error
906
+ };
907
+ return {
908
+ data: clean(response.map((password) => ({
909
+ enabled: password.enabled,
910
+ id: password.id,
911
+ value: password.smtp_password
912
+ }))),
913
+ error: null
914
+ };
915
+ }
916
+ async deleteSmtpPassword(handle, id) {
917
+ let error = null;
918
+ if (!handle) {
919
+ error = createError("No handle provided.");
920
+ return {
921
+ success: false,
922
+ error
923
+ };
924
+ }
925
+ await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/smtp-password/${id}`, { onResponseError: async ({ response }) => {
926
+ error = getStatusError(response, { [ErrorCode.BadRequest]: "Missing or invalid SMTP password ID." });
927
+ } }).catch((e) => {
928
+ error ||= getResultError(e, "Failed to delete sub-account SMTP password.");
929
+ });
930
+ return {
931
+ success: !error,
932
+ error
933
+ };
934
+ }
935
+ async getLimit(handle) {
936
+ let error = null;
937
+ if (!handle) {
938
+ error = createError("No handle provided.");
939
+ return {
940
+ data: null,
941
+ error
942
+ };
943
+ }
944
+ const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/limit`, { onResponseError: async ({ response }) => {
945
+ error = getStatusError(response, { [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.` });
946
+ } }).catch((e) => {
947
+ error ||= getResultError(e, "Failed to fetch sub-account limit.");
948
+ return null;
949
+ });
950
+ if (!response) return {
951
+ data: null,
952
+ error
953
+ };
954
+ return {
955
+ data: clean(response),
956
+ error: null
957
+ };
958
+ }
959
+ async setLimit(handle, limit) {
960
+ let error = null;
961
+ if (!handle) {
962
+ error = createError("No handle provided.");
963
+ return {
964
+ success: false,
965
+ error
966
+ };
967
+ }
968
+ await this.mailchannels.put(`/tx/v1/sub-account/${handle}/limit`, {
969
+ body: limit,
970
+ onResponseError: async ({ response }) => {
971
+ error = getStatusError(response, {
972
+ [ErrorCode.BadRequest]: "Bad Request.",
973
+ [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
974
+ });
975
+ }
976
+ }).catch((e) => {
977
+ error ||= getResultError(e, "Failed to set sub-account limit.");
978
+ });
979
+ return {
980
+ success: !error,
981
+ error
982
+ };
983
+ }
984
+ async deleteLimit(handle) {
985
+ let error = null;
986
+ if (!handle) {
987
+ error = createError("No handle provided.");
988
+ return {
989
+ success: false,
990
+ error
991
+ };
992
+ }
993
+ await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/limit`, { onResponseError: async ({ response }) => {
994
+ error = getStatusError(response, { [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.` });
995
+ } }).catch((e) => {
996
+ error ||= getResultError(e, "Failed to delete sub-account limit.");
997
+ });
998
+ return {
999
+ success: !error,
1000
+ error
1001
+ };
1002
+ }
1003
+ async getUsage(handle) {
1004
+ let error = null;
1005
+ if (!handle) {
1006
+ error = createError("No handle provided.");
1007
+ return {
1008
+ data: null,
1009
+ error
1010
+ };
1011
+ }
1012
+ const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/usage`, { onResponseError: async ({ response }) => {
1013
+ error = getStatusError(response, { [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.` });
1014
+ } }).catch((e) => {
1015
+ error ||= getResultError(e, "Failed to fetch sub-account usage.");
1016
+ return null;
1017
+ });
1018
+ if (!response) return {
1019
+ data: null,
1020
+ error
1021
+ };
1022
+ return {
1023
+ data: clean({
1024
+ endDate: response.period_end_date,
1025
+ startDate: response.period_start_date,
1026
+ total: response.total_usage
1027
+ }),
1028
+ error: null
1029
+ };
1030
+ }
1031
+ };
1032
+ var Metrics = class {
1033
+ constructor(mailchannels) {
1034
+ this.mailchannels = mailchannels;
1035
+ }
1036
+ async engagement(options) {
1037
+ let error = null;
1038
+ const response = await this.mailchannels.get("/tx/v1/metrics/engagement", {
1039
+ query: {
1040
+ start_time: options?.startTime,
1041
+ end_time: options?.endTime,
1042
+ campaign_id: options?.campaignId,
1043
+ interval: options?.interval
1044
+ },
1045
+ onResponseError: async ({ response }) => {
1046
+ error = getStatusError(response, { [ErrorCode.BadRequest]: "Bad Request." });
1047
+ }
1048
+ }).catch((e) => {
1049
+ error ||= getResultError(e, "Failed to fetch engagement metrics.");
1050
+ return null;
1051
+ });
1052
+ if (!response) return {
1053
+ data: null,
1054
+ error
1055
+ };
1056
+ return {
1057
+ data: clean({
1058
+ buckets: {
1059
+ click: mapBuckets(response.buckets.click),
1060
+ clickTrackingDelivered: mapBuckets(response.buckets.click_tracking_delivered),
1061
+ open: mapBuckets(response.buckets.open),
1062
+ openTrackingDelivered: mapBuckets(response.buckets.open_tracking_delivered)
1063
+ },
1064
+ click: response.click,
1065
+ clickTrackingDelivered: response.click_tracking_delivered,
1066
+ endTime: response.end_time,
1067
+ open: response.open,
1068
+ openTrackingDelivered: response.open_tracking_delivered,
1069
+ startTime: response.start_time
1070
+ }),
1071
+ error: null
1072
+ };
1073
+ }
1074
+ async performance(options) {
1075
+ let error = null;
1076
+ const response = await this.mailchannels.get("/tx/v1/metrics/performance", {
1077
+ query: {
1078
+ start_time: options?.startTime,
1079
+ end_time: options?.endTime,
1080
+ campaign_id: options?.campaignId,
1081
+ interval: options?.interval
1082
+ },
1083
+ onResponseError: async ({ response }) => {
1084
+ error = getStatusError(response, { [ErrorCode.BadRequest]: "Bad Request." });
1085
+ }
1086
+ }).catch((e) => {
1087
+ error ||= getResultError(e, "Failed to fetch performance metrics.");
1088
+ return null;
1089
+ });
1090
+ if (!response) return {
1091
+ data: null,
1092
+ error
1093
+ };
1094
+ return {
1095
+ data: clean({
1096
+ bounced: response.bounced,
1097
+ buckets: {
1098
+ bounced: mapBuckets(response.buckets.bounced),
1099
+ delivered: mapBuckets(response.buckets.delivered),
1100
+ processed: mapBuckets(response.buckets.processed)
1101
+ },
1102
+ delivered: response.delivered,
1103
+ endTime: response.end_time,
1104
+ processed: response.processed,
1105
+ startTime: response.start_time
1106
+ }),
1107
+ error: null
1108
+ };
1109
+ }
1110
+ async recipientBehaviour(options) {
1111
+ let error = null;
1112
+ const response = await this.mailchannels.get("/tx/v1/metrics/recipient-behaviour", {
1113
+ query: {
1114
+ start_time: options?.startTime,
1115
+ end_time: options?.endTime,
1116
+ campaign_id: options?.campaignId,
1117
+ interval: options?.interval
1118
+ },
1119
+ onResponseError: async ({ response }) => {
1120
+ error = getStatusError(response, { [ErrorCode.BadRequest]: "Bad Request." });
1121
+ }
1122
+ }).catch((e) => {
1123
+ error ||= getResultError(e, "Failed to fetch recipient behaviour metrics.");
1124
+ return null;
1125
+ });
1126
+ if (!response) return {
1127
+ data: null,
1128
+ error
1129
+ };
1130
+ return {
1131
+ data: clean({
1132
+ buckets: {
1133
+ unsubscribeDelivered: mapBuckets(response.buckets.unsubscribe_delivered),
1134
+ unsubscribed: mapBuckets(response.buckets.unsubscribed)
1135
+ },
1136
+ endTime: response.end_time,
1137
+ startTime: response.start_time,
1138
+ unsubscribeDelivered: response.unsubscribe_delivered,
1139
+ unsubscribed: response.unsubscribed
1140
+ }),
1141
+ error: null
1142
+ };
1143
+ }
1144
+ async volume(options) {
1145
+ let error = null;
1146
+ const response = await this.mailchannels.get("/tx/v1/metrics/volume", {
1147
+ query: {
1148
+ start_time: options?.startTime,
1149
+ end_time: options?.endTime,
1150
+ campaign_id: options?.campaignId,
1151
+ interval: options?.interval
1152
+ },
1153
+ onResponseError: async ({ response }) => {
1154
+ error = getStatusError(response, { [ErrorCode.BadRequest]: "Bad Request." });
1155
+ }
1156
+ }).catch((e) => {
1157
+ error ||= getResultError(e, "Failed to fetch volume metrics.");
1158
+ return null;
1159
+ });
1160
+ if (!response) return {
1161
+ data: null,
1162
+ error
1163
+ };
1164
+ return {
1165
+ data: clean({
1166
+ buckets: {
1167
+ delivered: mapBuckets(response.buckets.delivered),
1168
+ dropped: mapBuckets(response.buckets.dropped),
1169
+ processed: mapBuckets(response.buckets.processed)
1170
+ },
1171
+ delivered: response.delivered,
1172
+ dropped: response.dropped,
1173
+ endTime: response.end_time,
1174
+ processed: response.processed,
1175
+ startTime: response.start_time
1176
+ }),
1177
+ error: null
1178
+ };
1179
+ }
1180
+ async usage() {
1181
+ let error = null;
1182
+ const response = await this.mailchannels.get("/tx/v1/usage", { onResponseError: async ({ response }) => {
1183
+ error = getStatusError(response);
1184
+ } }).catch((e) => {
1185
+ error ||= getResultError(e, "Failed to fetch usage metrics.");
1186
+ return null;
1187
+ });
1188
+ if (!response) return {
1189
+ data: null,
1190
+ error
1191
+ };
1192
+ return {
1193
+ data: clean({
1194
+ endDate: response.period_end_date,
1195
+ startDate: response.period_start_date,
1196
+ total: response.total_usage
1197
+ }),
1198
+ error: null
1199
+ };
1200
+ }
1201
+ async senders(type, options) {
1202
+ let error = null;
1203
+ error = validatePagination({
1204
+ ...options,
1205
+ max: 1e3
1206
+ });
1207
+ if (error) return {
1208
+ data: null,
1209
+ error
1210
+ };
1211
+ const response = await this.mailchannels.get(`/tx/v1/metrics/senders/${type}`, {
1212
+ query: {
1213
+ start_time: options?.startTime,
1214
+ end_time: options?.endTime,
1215
+ limit: options?.limit,
1216
+ offset: options?.offset,
1217
+ sort_order: options?.sortOrder
1218
+ },
1219
+ onResponseError: async ({ response }) => {
1220
+ error = getStatusError(response, { [ErrorCode.BadRequest]: "Bad Request." });
1221
+ }
1222
+ }).catch((e) => {
1223
+ error ||= getResultError(e, "Failed to fetch senders metrics.");
1224
+ return null;
1225
+ });
1226
+ if (!response) return {
1227
+ data: null,
1228
+ error
1229
+ };
1230
+ return {
1231
+ data: clean({
1232
+ endTime: response.end_time,
1233
+ limit: response.limit,
1234
+ offset: response.offset,
1235
+ senders: response.senders,
1236
+ startTime: response.start_time,
1237
+ total: response.total
1238
+ }),
1239
+ error: null
1240
+ };
1241
+ }
1242
+ };
1243
+ var Suppressions = class {
1244
+ constructor(mailchannels) {
1245
+ this.mailchannels = mailchannels;
1246
+ }
1247
+ async create(options) {
1248
+ let error = null;
1249
+ const { addToSubAccounts, entries } = options;
1250
+ const payload = {
1251
+ add_to_sub_accounts: addToSubAccounts,
1252
+ suppression_entries: entries.map((entry) => ({
1253
+ notes: entry.notes,
1254
+ recipient: entry.recipient,
1255
+ suppression_types: Array.from(new Set(entry.types || ["non-transactional"]))
1256
+ }))
1257
+ };
1258
+ await this.mailchannels.post("/tx/v1/suppression-list", {
1259
+ body: payload,
1260
+ onResponseError: async ({ response }) => {
1261
+ error = getStatusError(response, {
1262
+ [ErrorCode.BadRequest]: "Bad Request.",
1263
+ [ErrorCode.Conflict]: "Conflict. One or more suppression entries in the request already exist and cannot be created again.",
1264
+ [ErrorCode.PayloadTooLarge]: "Payload too large. The request exceeds the maximum allowed total of 1000 suppression entries for the parent account and/or its sub-accounts."
1265
+ });
1266
+ }
1267
+ }).catch((e) => {
1268
+ error ||= getResultError(e, "Failed to create suppression entries.");
1269
+ });
1270
+ return {
1271
+ success: !error,
1272
+ error
1273
+ };
1274
+ }
1275
+ async delete(recipient, source) {
1276
+ let error = null;
1277
+ await this.mailchannels.delete(`/tx/v1/suppression-list/recipients/${recipient}`, {
1278
+ query: { source },
1279
+ onResponseError: async ({ response }) => {
1280
+ error = getStatusError(response, { [ErrorCode.BadRequest]: "Bad Request." });
1281
+ }
1282
+ }).catch((e) => {
1283
+ error ||= getResultError(e, "Failed to delete suppression entry.");
1284
+ });
1285
+ return {
1286
+ success: !error,
1287
+ error
1288
+ };
1289
+ }
1290
+ async list(options) {
1291
+ let error = null;
1292
+ error = validatePagination({
1293
+ ...options,
1294
+ max: 1e3
1295
+ });
1296
+ if (error) return {
1297
+ data: null,
1298
+ error
1299
+ };
1300
+ const payload = {
1301
+ recipient: options?.recipient,
1302
+ source: options?.source,
1303
+ created_before: options?.createdBefore,
1304
+ created_after: options?.createdAfter,
1305
+ limit: options?.limit,
1306
+ offset: options?.offset
1307
+ };
1308
+ const response = await this.mailchannels.get("/tx/v1/suppression-list", {
1309
+ query: payload,
1310
+ onResponseError: async ({ response }) => {
1311
+ error = getStatusError(response, { [ErrorCode.BadRequest]: "Bad Request." });
1312
+ }
1313
+ }).catch((e) => {
1314
+ error ||= getResultError(e, "Failed to fetch suppression entries.");
1315
+ return null;
1316
+ });
1317
+ if (!response) return {
1318
+ data: null,
1319
+ error
1320
+ };
1321
+ return {
1322
+ data: clean(response.suppression_list.map((entry) => ({
1323
+ createdAt: entry.created_at,
1324
+ notes: entry.notes,
1325
+ recipient: entry.recipient,
1326
+ sender: entry.sender,
1327
+ source: entry.source,
1328
+ types: entry.suppression_types
1329
+ }))),
1330
+ error: null
1331
+ };
1332
+ }
1333
+ };
1334
+ var Domains = class {
1335
+ constructor(mailchannels) {
1336
+ this.mailchannels = mailchannels;
1337
+ }
1338
+ async provision(options) {
1339
+ let error = null;
1340
+ const { associateKey, overwrite, ...payload } = options;
1341
+ const response = await this.mailchannels.post("/inbound/v1/domains", {
1342
+ query: {
1343
+ "associate-key": associateKey,
1344
+ "overwrite": overwrite
1345
+ },
1346
+ body: payload,
1347
+ onResponseError: async ({ response }) => {
1348
+ error = getStatusError(response, {
1349
+ [ErrorCode.BadRequest]: "Bad Request, returned in the case that an error occurs while converting an A-label domain to a U-label domain name.",
1350
+ [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.",
1351
+ [ErrorCode.Conflict]: `The domain '${options.domain}' is already provisioned, and is associated with a different customer.`
1352
+ });
1353
+ }
1354
+ }).catch((e) => {
1355
+ error ||= getResultError(e, "Failed to provision domain.");
1356
+ return null;
1357
+ });
1358
+ if (!response) return {
1359
+ data: null,
1360
+ error
1361
+ };
1362
+ return {
1363
+ data: clean(response),
1364
+ error: null
1365
+ };
1366
+ }
1367
+ async bulkProvision(options, domains) {
1368
+ let error = null;
1369
+ const { associateKey, overwrite, subscriptionHandle } = options;
1370
+ if (!domains || !domains.length) {
1371
+ error = createError("No domains provided.");
1372
+ return {
1373
+ data: null,
1374
+ error
1375
+ };
1376
+ }
1377
+ if (domains.length > 1e3) {
1378
+ error = createError("The maximum number of domains to be provisioned is 1000.");
1379
+ return {
1380
+ data: null,
1381
+ error
1382
+ };
1383
+ }
1384
+ const response = await this.mailchannels.post("/inbound/v1/domains/batch", {
1385
+ query: {
1386
+ subscriptionHandle,
1387
+ "associate-key": associateKey,
1388
+ "overwrite": overwrite
1389
+ },
1390
+ body: { domains },
1391
+ onResponseError: async ({ response }) => {
1392
+ error = getStatusError(response, {
1393
+ [ErrorCode.BadRequest]: "Bad Request, returned in the case that a domain name fails RFC 5891 validation.",
1394
+ [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."
1395
+ });
1396
+ }
1397
+ }).catch((e) => {
1398
+ error ||= getResultError(e, "Failed to provision domains.");
1399
+ return null;
1400
+ });
1401
+ if (!response) return {
1402
+ data: null,
1403
+ error
1404
+ };
1405
+ return {
1406
+ data: clean(response),
1407
+ error: null
1408
+ };
1409
+ }
1410
+ async list(options) {
1411
+ let error = null;
1412
+ error = validatePagination({
1413
+ ...options,
1414
+ max: 5e3
1415
+ });
1416
+ if (error) return {
1417
+ data: null,
1418
+ error
1419
+ };
1420
+ const response = await this.mailchannels.get("/inbound/v1/domains", {
1421
+ query: options,
1422
+ onResponseError: async ({ response }) => {
1423
+ error = getStatusError(response);
1424
+ }
1425
+ }).catch((e) => {
1426
+ error ||= getResultError(e, "Failed to fetch domains.");
1427
+ return null;
1428
+ });
1429
+ if (!response) return {
1430
+ data: null,
1431
+ error
1432
+ };
1433
+ return {
1434
+ data: clean({
1435
+ domains: response.domains,
1436
+ total: response.total
1437
+ }),
1438
+ error: null
1439
+ };
1440
+ }
1441
+ async delete(domain) {
1442
+ let error = null;
1443
+ if (!domain) {
1444
+ error = createError("No domain provided.");
1445
+ return {
1446
+ success: false,
1447
+ error
1448
+ };
1449
+ }
1450
+ await this.mailchannels.delete(`/inbound/v1/domains/${domain}`, { onResponseError: async ({ response }) => {
1451
+ error = getStatusError(response, {
1452
+ [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.",
1453
+ [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1454
+ });
1455
+ } }).catch((e) => {
1456
+ error ||= getResultError(e, "Failed to delete domain.");
1457
+ });
1458
+ return {
1459
+ success: !error,
1460
+ error
1461
+ };
1462
+ }
1463
+ async addListEntry(domain, options) {
1464
+ const { listName, item } = options;
1465
+ let error = null;
1466
+ if (!domain) {
1467
+ error = createError("No domain provided.");
1468
+ return {
1469
+ data: null,
1470
+ error
1471
+ };
1472
+ }
1473
+ if (!listName) {
1474
+ error = createError("No list name provided.");
1475
+ return {
1476
+ data: null,
1477
+ error
1478
+ };
1479
+ }
1480
+ const response = await this.mailchannels.post(`/inbound/v1/domains/${domain}/lists/${listName}`, {
1481
+ body: { item },
1482
+ onResponseError: async ({ response }) => {
1483
+ error = getStatusError(response, {
1484
+ [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.",
1485
+ [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1486
+ });
1487
+ }
1488
+ }).catch((e) => {
1489
+ error ||= getResultError(e, "Failed to add domain list entry.");
1490
+ return null;
1491
+ });
1492
+ if (!response) return {
1493
+ data: null,
1494
+ error
1495
+ };
1496
+ return {
1497
+ data: clean({
1498
+ action: response.action,
1499
+ item: response.item,
1500
+ type: response.item_type
1501
+ }),
1502
+ error: null
1503
+ };
1504
+ }
1505
+ async listEntries(domain, listName) {
1506
+ let error = null;
1507
+ if (!domain) {
1508
+ error = createError("No domain provided.");
1509
+ return {
1510
+ data: null,
1511
+ error
1512
+ };
1513
+ }
1514
+ if (!listName) {
1515
+ error = createError("No list name provided.");
1516
+ return {
1517
+ data: null,
1518
+ error
1519
+ };
1520
+ }
1521
+ const response = await this.mailchannels.get(`/inbound/v1/domains/${domain}/lists/${listName}`, { onResponseError: async ({ response }) => {
1522
+ error = getStatusError(response, {
1523
+ [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.",
1524
+ [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1525
+ });
1526
+ } }).catch((e) => {
1527
+ error ||= getResultError(e, "Failed to fetch domain list entries.");
1528
+ return null;
1529
+ });
1530
+ if (!response) return {
1531
+ data: null,
1532
+ error
1533
+ };
1534
+ return {
1535
+ data: clean(response.map(({ action, item, item_type }) => ({
1536
+ action,
1537
+ item,
1538
+ type: item_type
1539
+ }))),
1540
+ error: null
1541
+ };
1542
+ }
1543
+ async deleteListEntry(domain, options) {
1544
+ const { listName, item } = options;
1545
+ let error = null;
1546
+ if (!domain) {
1547
+ error = createError("No domain provided.");
1548
+ return {
1549
+ success: false,
1550
+ error
1551
+ };
1552
+ }
1553
+ if (!listName) {
1554
+ error = createError("No list name provided.");
1555
+ return {
1556
+ success: false,
1557
+ error
1558
+ };
1559
+ }
1560
+ await this.mailchannels.delete(`/inbound/v1/domains/${domain}/lists/${listName}`, {
1561
+ query: { item },
1562
+ onResponseError: async ({ response }) => {
1563
+ error = getStatusError(response, {
1564
+ [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.",
1565
+ [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1566
+ });
1567
+ }
1568
+ }).catch((e) => {
1569
+ error ||= getResultError(e, "Failed to delete domain list entry.");
1570
+ });
1571
+ return {
1572
+ success: !error,
1573
+ error
1574
+ };
1575
+ }
1576
+ async createLoginLink(domain) {
1577
+ let error = null;
1578
+ if (!domain) {
1579
+ error = createError("No domain provided.");
1580
+ return {
1581
+ data: null,
1582
+ error
1583
+ };
1584
+ }
1585
+ const response = await this.mailchannels.get(`/inbound/v1/domains/${domain}/login-link`, { onResponseError: async ({ response }) => {
1586
+ error = getStatusError(response, {
1587
+ [ErrorCode.Unauthorized]: "The domain does not belong to this customer.",
1588
+ [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.",
1589
+ [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1590
+ });
1591
+ } }).catch((e) => {
1592
+ error ||= getResultError(e, "Failed to create login link.");
1593
+ return null;
1594
+ });
1595
+ if (!response) return {
1596
+ data: null,
1597
+ error
1598
+ };
1599
+ return {
1600
+ data: clean({ link: response.loginLink }),
1601
+ error: null
1602
+ };
1603
+ }
1604
+ async setDownstreamAddress(domain, records) {
1605
+ let error = null;
1606
+ if (!domain) {
1607
+ error = createError("No domain provided.");
1608
+ return {
1609
+ success: false,
1610
+ error
1611
+ };
1612
+ }
1613
+ if (!records) {
1614
+ error = createError("No records provided.");
1615
+ return {
1616
+ success: false,
1617
+ error
1618
+ };
1619
+ }
1620
+ if (records.length > 10) {
1621
+ error = createError("The maximum of records to be set is 10.");
1622
+ return {
1623
+ success: false,
1624
+ error
1625
+ };
1626
+ }
1627
+ await this.mailchannels.put(`/inbound/v1/domains/${domain}/downstream-address`, {
1628
+ body: { records },
1629
+ onResponseError: async ({ response }) => {
1630
+ error = getStatusError(response, {
1631
+ [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.",
1632
+ [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1633
+ });
1634
+ }
1635
+ }).catch((e) => {
1636
+ error ||= getResultError(e, "Failed to set downstream address.");
1637
+ });
1638
+ return {
1639
+ success: !error,
1640
+ error
1641
+ };
1642
+ }
1643
+ async listDownstreamAddresses(domain, options) {
1644
+ let error = null;
1645
+ if (!domain) {
1646
+ error = createError("No domain provided.");
1647
+ return {
1648
+ data: null,
1649
+ error
1650
+ };
1651
+ }
1652
+ error = validatePagination(options);
1653
+ if (error) return {
1654
+ data: null,
1655
+ error
1656
+ };
1657
+ const response = await this.mailchannels.get(`/inbound/v1/domains/${domain}/downstream-address`, {
1658
+ query: options,
1659
+ onResponseError: async ({ response }) => {
1660
+ error = getStatusError(response, {
1661
+ [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.",
1662
+ [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1663
+ });
1664
+ }
1665
+ }).catch((e) => {
1666
+ error ||= getResultError(e, "Failed to list downstream addresses.");
1667
+ return null;
1668
+ });
1669
+ if (!response) return {
1670
+ data: null,
1671
+ error
1672
+ };
1673
+ return {
1674
+ data: clean(response.records),
1675
+ error: null
1676
+ };
1677
+ }
1678
+ async updateApiKey(domain, key) {
1679
+ let error = null;
1680
+ if (!domain) {
1681
+ error = createError("No domain provided.");
1682
+ return {
1683
+ success: false,
1684
+ error
1685
+ };
1686
+ }
1687
+ if (!key) {
1688
+ error = createError("No API key provided.");
1689
+ return {
1690
+ success: false,
1691
+ error
1692
+ };
1693
+ }
1694
+ await this.mailchannels.put(`/inbound/v1/domains/${domain}/api-key`, {
1695
+ body: { apiKey: key },
1696
+ onResponseError: async ({ response }) => {
1697
+ error = getStatusError(response, {
1698
+ [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.",
1699
+ [ErrorCode.NotFound]: "The domain does not exist."
1700
+ });
1701
+ }
1702
+ }).catch((e) => {
1703
+ error ||= getResultError(e, "Failed to update domain API key.");
1704
+ });
1705
+ return {
1706
+ success: !error,
1707
+ error
1708
+ };
1709
+ }
1710
+ async bulkCreateLoginLinks(domains) {
1711
+ let error = null;
1712
+ if (!domains || !domains.length) {
1713
+ error = createError("No domains provided.");
1714
+ return {
1715
+ data: null,
1716
+ error
1717
+ };
1718
+ }
1719
+ if (domains.length > 1e3) {
1720
+ error = createError("The maximum number of domains to create login links for is 1000.");
1721
+ return {
1722
+ data: null,
1723
+ error
1724
+ };
1725
+ }
1726
+ const response = await this.mailchannels.post("/inbound/v1/domains/batch/login-link", {
1727
+ body: { domains: domains.map((domain) => ({ domain })) },
1728
+ onResponseError: async ({ response }) => {
1729
+ error = getStatusError(response, { [ErrorCode.BadRequest]: "Bad Request." });
1730
+ }
1731
+ }).catch((e) => {
1732
+ error ||= getResultError(e, "Failed to create login links.");
1733
+ return null;
1734
+ });
1735
+ if (!response) return {
1736
+ data: null,
1737
+ error
1738
+ };
1739
+ return {
1740
+ data: clean(response),
1741
+ error: null
1742
+ };
1743
+ }
1744
+ };
1745
+ var Lists = class {
1746
+ constructor(mailchannels) {
1747
+ this.mailchannels = mailchannels;
1748
+ }
1749
+ async addListEntry(options) {
1750
+ let error = null;
1751
+ const { listName, item } = options;
1752
+ if (!listName) {
1753
+ error = createError("No list name provided.");
1754
+ return {
1755
+ data: null,
1756
+ error
1757
+ };
1758
+ }
1759
+ const response = await this.mailchannels.post(`/inbound/v1/lists/${listName}`, {
1760
+ body: { item },
1761
+ onResponseError: async ({ response }) => {
1762
+ error = getStatusError(response);
1763
+ }
1764
+ }).catch((e) => {
1765
+ error ||= getResultError(e, "Failed to add list entry.");
1766
+ return null;
1767
+ });
1768
+ if (!response) return {
1769
+ data: null,
1770
+ error
1771
+ };
1772
+ return {
1773
+ data: clean({
1774
+ action: response.action,
1775
+ item: response.item,
1776
+ type: response.item_type
1777
+ }),
1778
+ error: null
1779
+ };
1780
+ }
1781
+ async listEntries(listName) {
1782
+ let error = null;
1783
+ if (!listName) {
1784
+ error = createError("No list name provided.");
1785
+ return {
1786
+ data: null,
1787
+ error
1788
+ };
1789
+ }
1790
+ const response = await this.mailchannels.get(`/inbound/v1/lists/${listName}`, { onResponseError: async ({ response }) => {
1791
+ error = getStatusError(response);
1792
+ } }).catch((e) => {
1793
+ error ||= getResultError(e, "Failed to fetch list entries.");
1794
+ return null;
1795
+ });
1796
+ if (!response) return {
1797
+ data: null,
1798
+ error
1799
+ };
1800
+ return {
1801
+ data: clean(response.map(({ action, item, item_type }) => ({
1802
+ action,
1803
+ item,
1804
+ type: item_type
1805
+ }))),
1806
+ error: null
1807
+ };
1808
+ }
1809
+ async deleteListEntry(options) {
1810
+ const { listName, item } = options;
1811
+ let error = null;
1812
+ if (!listName) {
1813
+ error = createError("No list name provided.");
1814
+ return {
1815
+ success: false,
1816
+ error
1817
+ };
1818
+ }
1819
+ await this.mailchannels.delete(`/inbound/v1/lists/${listName}`, {
1820
+ query: { item },
1821
+ onResponseError: async ({ response }) => {
1822
+ error = getStatusError(response);
1823
+ }
1824
+ }).catch((e) => {
1825
+ error ||= getResultError(e, "Failed to delete list entry.");
1826
+ });
1827
+ return {
1828
+ success: !error,
1829
+ error
1830
+ };
1831
+ }
1832
+ };
1833
+ var Users = class {
1834
+ constructor(mailchannels) {
1835
+ this.mailchannels = mailchannels;
1836
+ }
1837
+ async create(email, options) {
1838
+ const { admin, filter, listEntries } = options || {};
1839
+ let error = null;
1840
+ if (!email) {
1841
+ error = createError("No email address provided.");
1842
+ return {
1843
+ data: null,
1844
+ error
1845
+ };
1846
+ }
1847
+ const response = await this.mailchannels.put("/inbound/v1/users", {
1848
+ query: {
1849
+ email_address: email,
1850
+ admin: Boolean(admin),
1851
+ filter
1852
+ },
1853
+ body: { list_entries: listEntries },
1854
+ onResponseError: async ({ response }) => {
1855
+ error = getStatusError(response, { [ErrorCode.BadRequest]: `The email address '${email}' is invalid.` });
1856
+ }
1857
+ }).catch((e) => {
1858
+ error ||= getResultError(e, "Failed to create user.");
1859
+ return null;
1860
+ });
1861
+ if (!response) return {
1862
+ data: null,
1863
+ error
1864
+ };
1865
+ return {
1866
+ data: clean({
1867
+ email: response.recipient.email_address,
1868
+ roles: response.recipient.roles,
1869
+ filter: response.recipient.filter,
1870
+ listEntries: response.list_entries.map(({ item, item_type, action }) => ({
1871
+ item,
1872
+ type: item_type,
1873
+ action
1874
+ }))
1875
+ }),
1876
+ error: null
1877
+ };
1878
+ }
1879
+ async addListEntry(email, options) {
1880
+ const { listName, item } = options;
1881
+ let error = null;
1882
+ if (!email) {
1883
+ error = createError("No email provided.");
1884
+ return {
1885
+ data: null,
1886
+ error
1887
+ };
1888
+ }
1889
+ if (!listName) {
1890
+ error = createError("No list name provided.");
1891
+ return {
1892
+ data: null,
1893
+ error
1894
+ };
1895
+ }
1896
+ const response = await this.mailchannels.post(`/inbound/v1/users/${email}/lists/${listName}`, {
1897
+ body: { item },
1898
+ onResponseError: async ({ response }) => {
1899
+ error = getStatusError(response, {
1900
+ [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.",
1901
+ [ErrorCode.NotFound]: `The recipient '${email}' was not found.`
1902
+ });
1903
+ }
1904
+ }).catch((e) => {
1905
+ error ||= getResultError(e, "Failed to add user list entry.");
1906
+ return null;
1907
+ });
1908
+ if (!response) return {
1909
+ data: null,
1910
+ error
1911
+ };
1912
+ return {
1913
+ data: clean({
1914
+ action: response.action,
1915
+ item: response.item,
1916
+ type: response.item_type
1917
+ }),
1918
+ error: null
1919
+ };
1920
+ }
1921
+ async listEntries(email, listName) {
1922
+ let error = null;
1923
+ if (!email) {
1924
+ error = createError("No email provided.");
1925
+ return {
1926
+ data: null,
1927
+ error
1928
+ };
1929
+ }
1930
+ if (!listName) {
1931
+ error = createError("No list name provided.");
1932
+ return {
1933
+ data: null,
1934
+ error
1935
+ };
1936
+ }
1937
+ const response = await this.mailchannels.get(`/inbound/v1/users/${email}/lists/${listName}`, { onResponseError: async ({ response }) => {
1938
+ error = getStatusError(response, {
1939
+ [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.",
1940
+ [ErrorCode.NotFound]: `The recipient '${email}' was not found.`
1941
+ });
1942
+ } }).catch((e) => {
1943
+ error ||= getResultError(e, "Failed to fetch user list entries.");
1944
+ return null;
1945
+ });
1946
+ if (!response) return {
1947
+ data: null,
1948
+ error
1949
+ };
1950
+ return {
1951
+ data: clean(response.map(({ action, item, item_type }) => ({
1952
+ action,
1953
+ item,
1954
+ type: item_type
1955
+ }))),
1956
+ error: null
1957
+ };
1958
+ }
1959
+ async deleteListEntry(email, options) {
1960
+ const { listName, item } = options;
1961
+ let error = null;
1962
+ if (!email) {
1963
+ error = createError("No email provided.");
1964
+ return {
1965
+ success: false,
1966
+ error
1967
+ };
1968
+ }
1969
+ if (!listName) {
1970
+ error = createError("No list name provided.");
1971
+ return {
1972
+ success: false,
1973
+ error
1974
+ };
1975
+ }
1976
+ await this.mailchannels.delete(`/inbound/v1/users/${email}/lists/${listName}`, {
1977
+ query: { item },
1978
+ onResponseError: async ({ response }) => {
1979
+ error = getStatusError(response, {
1980
+ [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.",
1981
+ [ErrorCode.NotFound]: `The recipient '${email}' was not found.`
1982
+ });
1983
+ }
1984
+ }).catch((e) => {
1985
+ error ||= getResultError(e, "Failed to delete user list entry.");
1986
+ });
1987
+ return {
1988
+ success: !error,
1989
+ error
1990
+ };
1991
+ }
1992
+ };
1993
+ var Service = class {
1994
+ constructor(mailchannels) {
1995
+ this.mailchannels = mailchannels;
1996
+ }
1997
+ async status() {
1998
+ let error = null;
1999
+ await this.mailchannels.get("/inbound/v1/status", { onResponseError: async ({ response }) => {
2000
+ error = getStatusError(response);
2001
+ } }).catch((e) => {
2002
+ error ||= getResultError(e, "Failed to fetch service status.");
2003
+ });
2004
+ return {
2005
+ success: !error,
2006
+ error
2007
+ };
2008
+ }
2009
+ async subscriptions() {
2010
+ let error = null;
2011
+ const response = await this.mailchannels.get("/inbound/v1/subscriptions", { onResponseError: async ({ response }) => {
2012
+ error = getStatusError(response, { [ErrorCode.NotFound]: "We could not find a customer that matched the customerHandle." });
2013
+ } }).catch((e) => {
2014
+ error ||= getResultError(e, "Failed to fetch subscriptions.");
2015
+ return null;
2016
+ });
2017
+ if (!response) return {
2018
+ data: null,
2019
+ error
2020
+ };
2021
+ return {
2022
+ data: clean(response),
2023
+ error: null
2024
+ };
2025
+ }
2026
+ async report(options) {
2027
+ let error = null;
2028
+ const { type, ...payload } = options;
2029
+ await this.mailchannels.post("/inbound/v1/report", {
2030
+ query: { report_type: type },
2031
+ body: payload,
2032
+ onResponseError: async ({ response }) => {
2033
+ error = getStatusError(response);
2034
+ }
2035
+ }).catch((e) => {
2036
+ error ||= getResultError(e, "Failed to submit report.");
2037
+ });
2038
+ return {
2039
+ success: !error,
2040
+ error
2041
+ };
2042
+ }
2043
+ };
2044
+ var MailChannels = class extends MailChannelsClient {
2045
+ emails = new Emails(this);
2046
+ webhooks = new Webhooks(this);
2047
+ subAccounts = new SubAccounts(this);
2048
+ metrics = new Metrics(this);
2049
+ suppressions = new Suppressions(this);
2050
+ domains = new Domains(this);
2051
+ lists = new Lists(this);
2052
+ users = new Users(this);
2053
+ service = new Service(this);
2054
+ constructor(key) {
2055
+ super(key);
2056
+ }
145
2057
  };
146
-
147
- class Emails {
148
- constructor(mailchannels) {
149
- this.mailchannels = mailchannels;
150
- }
151
- async _sendEmail(options, flags) {
152
- let error = null;
153
- const { cc, bcc, from, to, html, text, mustaches, dkim } = options;
154
- const parsedFrom = parseRecipient(from);
155
- if (!parsedFrom || !parsedFrom.email) {
156
- error = createError("No sender provided. Use the `from` option to specify a sender");
157
- return { success: false, data: null, error };
158
- }
159
- const parsedTo = parseArrayRecipients(to);
160
- if (!parsedTo || !parsedTo.length) {
161
- error = createError("No recipients provided. Use the `to` option to specify at least one recipient");
162
- return { success: false, data: null, error };
163
- }
164
- if (!text && !html) {
165
- error = createError("No email content provided");
166
- return { success: false, data: null, error };
167
- }
168
- const content = [];
169
- const template_type = mustaches ? "mustache" : void 0;
170
- if (text) content.push({ type: "text/plain", value: text, template_type });
171
- if (html) content.push({ type: "text/html", value: html, template_type });
172
- const payload = {
173
- attachments: options.attachments,
174
- campaign_id: options.campaignId,
175
- personalizations: [{
176
- bcc: parseArrayRecipients(bcc),
177
- cc: parseArrayRecipients(cc),
178
- to: parsedTo,
179
- dkim_domain: dkim?.domain || void 0,
180
- dkim_private_key: dkim?.privateKey ? stripPemHeaders(dkim.privateKey) : void 0,
181
- dkim_selector: dkim?.selector || void 0,
182
- dynamic_template_data: options.mustaches
183
- }],
184
- headers: options.headers,
185
- reply_to: parseRecipient(options.replyTo),
186
- envelope_from: parseRecipient(options.envelopeFrom),
187
- from: parsedFrom,
188
- subject: options.subject,
189
- content,
190
- tracking_settings: options.tracking ? {
191
- click_tracking: options.tracking.click ? { enable: options.tracking.click } : void 0,
192
- open_tracking: options.tracking.open ? { enable: options.tracking.open } : void 0
193
- } : void 0,
194
- transactional: options.transactional
195
- };
196
- const endpoint = flags.async ? "/tx/v1/send-async" : "/tx/v1/send";
197
- const response = await this.mailchannels.post(endpoint, {
198
- query: { "dry-run": flags.dryRun },
199
- body: payload,
200
- onResponseError: async ({ response: response2 }) => {
201
- error = getStatusError(response2, {
202
- [ErrorCode.BadRequest]: "Bad Request.",
203
- [ErrorCode.Forbidden]: "User does not have access to this feature.",
204
- [ErrorCode.PayloadTooLarge]: "The total message size should not exceed 30MB. This includes the message itself, headers, and the combined size of any attachments."
205
- });
206
- }
207
- }).catch((e) => {
208
- error ||= getResultError(e, flags.async ? "Failed to queue email." : "Failed to send email.");
209
- return null;
210
- });
211
- if (!response) return { success: false, data: null, error };
212
- if (flags.async) {
213
- const asyncResponse = response;
214
- const data2 = clean({
215
- queuedAt: asyncResponse.queued_at,
216
- requestId: asyncResponse.request_id
217
- });
218
- return { data: data2, error: null };
219
- }
220
- const syncResponse = response;
221
- const data = clean({
222
- rendered: syncResponse.data,
223
- requestId: syncResponse.request_id,
224
- results: syncResponse.results?.map((result) => ({
225
- index: result.index,
226
- messageId: result.message_id,
227
- reason: result.reason,
228
- status: result.status
229
- }))
230
- });
231
- return { success: !!data, data, error: null };
232
- }
233
- /**
234
- * Sends an email message to one or more recipients.
235
- * @param options - The email options to send.
236
- * @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`.
237
- * @example
238
- * ```ts
239
- * const mailchannels = new MailChannels('your-api-key')
240
- * const { success, data, error } = await mailchannels.emails.send({
241
- * to: 'to@example.com',
242
- * from: 'from@example.com',
243
- * subject: 'Test',
244
- * html: 'Test'
245
- * })
246
- * ```
247
- */
248
- async send(options, dryRun = false) {
249
- return this._sendEmail(options, { dryRun });
250
- }
251
- /**
252
- * Queues an email message for asynchronous processing and returns immediately with a request ID.
253
- *
254
- * The email will be processed in the background, and you'll receive webhook events for all delivery status updates (e.g. `dropped`, `processed`, `delivered`, `hard-bounced`). These webhook events are identical to those sent for the synchronous /send endpoint.
255
- *
256
- * Use this endpoint when you need to send emails without waiting for processing to complete. This can improve your application's response time, especially when sending to multiple recipients.
257
- * @param options - The email options to send.
258
- * @example
259
- * ```ts
260
- * const mailchannels = new MailChannels('your-api-key')
261
- * const { data, error } = await mailchannels.emails.sendAsync({
262
- * to: 'to@example.com',
263
- * from: 'from@example.com',
264
- * subject: 'Test',
265
- * html: 'Test'
266
- * })
267
- * ```
268
- */
269
- async sendAsync(options) {
270
- return this._sendEmail(options, { async: true });
271
- }
272
- /**
273
- * 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.
274
- * @param options - The domain options to check.
275
- * @example
276
- * ```ts
277
- * const mailchannels = new MailChannels('your-api-key')
278
- * const { data, error } = await mailchannels.emails.checkDomain({
279
- * dkim: [{
280
- * domain: 'example.com',
281
- * privateKey: 'your-private-key',
282
- * selector: 'mailchannels'
283
- * }],
284
- * domain: 'example.com',
285
- * senderId: 'sender-id'
286
- * })
287
- * ```
288
- */
289
- async checkDomain(options) {
290
- let error = null;
291
- const { dkim, domain, senderId } = options;
292
- const dkimOptions = dkim ? Array.isArray(dkim) ? dkim : [dkim] : void 0;
293
- const payload = {
294
- dkim_settings: dkimOptions?.map(({ domain: domain2, privateKey, selector }) => ({
295
- dkim_domain: domain2,
296
- dkim_private_key: privateKey ? stripPemHeaders(privateKey) : void 0,
297
- dkim_selector: selector
298
- })),
299
- domain,
300
- sender_id: senderId
301
- };
302
- const response = await this.mailchannels.post("/tx/v1/check-domain", {
303
- body: payload,
304
- onResponseError: async ({ response: response2 }) => {
305
- error = getStatusError(response2, {
306
- [ErrorCode.BadRequest]: "Bad Request.",
307
- [ErrorCode.Forbidden]: "User does not have access to this feature."
308
- });
309
- }
310
- }).catch((e) => {
311
- error ||= getResultError(e, "Failed to check domain.");
312
- return null;
313
- });
314
- if (!response) return { data: null, error };
315
- const data = clean({
316
- dkim: response.check_results.dkim.map((dkimResults) => ({
317
- domain: dkimResults.dkim_domain,
318
- keyStatus: dkimResults.dkim_key_status,
319
- selector: dkimResults.dkim_selector,
320
- reason: dkimResults.reason,
321
- verdict: dkimResults.verdict
322
- })),
323
- domainLockdown: response.check_results.domain_lockdown,
324
- senderDomain: response.check_results.sender_domain,
325
- spf: response.check_results.spf,
326
- references: response.references
327
- });
328
- return { data, error: null };
329
- }
330
- /**
331
- * Create a DKIM key pair for a specified domain and selector using the specified algorithm and key length, for the current customer.
332
- * @param domain - The domain to create the DKIM key for.
333
- * @param options - DKIM key creation options.
334
- * @example
335
- * ```ts
336
- * const mailchannels = new MailChannels('your-api-key')
337
- * const { data, error } = await mailchannels.emails.createDkimKey('example.com', {
338
- * selector: 'mailchannels'
339
- * })
340
- * ```
341
- */
342
- async createDkimKey(domain, options) {
343
- let error = null;
344
- if (!options.selector || options.selector.length > 63) {
345
- error = createError("Selector must be between 1 and 63 characters.");
346
- return { data: null, error };
347
- }
348
- const payload = {
349
- algorithm: options.algorithm,
350
- key_length: options.length,
351
- selector: options.selector
352
- };
353
- const response = await this.mailchannels.post(`/tx/v1/domains/${domain}/dkim-keys`, {
354
- body: payload,
355
- onResponseError: async ({ response: response2 }) => {
356
- error = getStatusError(response2, {
357
- [ErrorCode.BadRequest]: "Bad Request.",
358
- [ErrorCode.Conflict]: "Key pair already created for domain, and selector."
359
- });
360
- }
361
- }).catch((e) => {
362
- error ||= getResultError(e, "Failed to create DKIM key.");
363
- return null;
364
- });
365
- if (!response) return { data: null, error };
366
- const data = clean({
367
- algorithm: response.algorithm,
368
- createdAt: response.created_at,
369
- dnsRecords: response.dkim_dns_records,
370
- domain: response.domain,
371
- gracePeriodExpiresAt: response.gracePeriodExpiresAt,
372
- length: response.key_length,
373
- publicKey: response.public_key,
374
- retiresAt: response.retiresAt,
375
- selector: response.selector,
376
- status: response.status,
377
- statusModifiedAt: response.status_modified_at
378
- });
379
- return { data, error: null };
380
- }
381
- /**
382
- * Search for DKIM keys by domain, with optional filters. If selector is provided, at most one key will be returned.
383
- * @param domain - The domain to search DKIM keys for.
384
- * @param options - The options to filter DKIM keys by.
385
- * @example
386
- * ```ts
387
- * const mailchannels = new MailChannels('your-api-key')
388
- * const { data, error } = await mailchannels.getDkimKeys('example.com', {
389
- * includeDnsRecord: true
390
- * })
391
- * ```
392
- */
393
- async getDkimKeys(domain, options) {
394
- let error = null;
395
- if (options?.selector && options.selector.length > 63) {
396
- error = createError("Selector must be between 1 and 63 characters.");
397
- return { data: null, error };
398
- }
399
- error = validateLimit(options?.limit, 100) || validateOffset(options?.offset);
400
- if (error) return { data: null, error };
401
- const payload = {
402
- selector: options?.selector,
403
- status: options?.status,
404
- offset: options?.offset,
405
- limit: options?.limit,
406
- include_dns_record: options?.includeDnsRecord
407
- };
408
- const response = await this.mailchannels.get(`/tx/v1/domains/${domain}/dkim-keys`, {
409
- query: payload,
410
- onResponseError: async ({ response: response2 }) => {
411
- error = getStatusError(response2, {
412
- [ErrorCode.BadRequest]: "Bad Request."
413
- });
414
- }
415
- }).catch((e) => {
416
- error ||= getResultError(e, "Failed to fetch DKIM keys.");
417
- return null;
418
- });
419
- if (!response) return { data: null, error };
420
- const data = clean(response.keys.map((key) => ({
421
- algorithm: key.algorithm,
422
- createdAt: key.created_at,
423
- dnsRecords: key.dkim_dns_records,
424
- domain: key.domain,
425
- gracePeriodExpiresAt: key.gracePeriodExpiresAt,
426
- length: key.key_length,
427
- publicKey: key.public_key,
428
- retiresAt: key.retiresAt,
429
- selector: key.selector,
430
- status: key.status,
431
- statusModifiedAt: key.status_modified_at
432
- })));
433
- return { data, error: null };
434
- }
435
- /**
436
- * Update fields of an existing DKIM key pair for the specified domain and selector, for the current customer. Currently, only the `status` field can be updated.
437
- * @param domain - The domain the DKIM key belongs to.
438
- * @param options - The options to update the DKIM key.
439
- * @example
440
- * ```ts
441
- * const mailchannels = new MailChannels('your-api-key')
442
- * const { success, error } = await mailchannels.emails.updateDkimKey('example.com', {
443
- * selector: 'mailchannels',
444
- * status: 'retired'
445
- * })
446
- */
447
- async updateDkimKey(domain, options) {
448
- let error = null;
449
- if (!options.selector || options.selector.length > 63) {
450
- error = createError("Selector must be between 1 and 63 characters.");
451
- return { success: false, error };
452
- }
453
- const payload = {
454
- status: options.status
455
- };
456
- await this.mailchannels.patch(`/tx/v1/domains/${domain}/dkim-keys/${options.selector}`, {
457
- body: payload,
458
- onResponseError: async ({ response }) => {
459
- error = getStatusError(response, {
460
- [ErrorCode.BadRequest]: "Bad Request.",
461
- [ErrorCode.NotFound]: "Specified key pair not found, or no active key for rotation. This may also occur if the DKIM domain or selector path parameter is missing."
462
- });
463
- }
464
- }).catch((e) => {
465
- error ||= getResultError(e, "Failed to update DKIM key.");
466
- });
467
- return { success: !error, error };
468
- }
469
- /**
470
- * Rotate an active DKIM key pair. Mark the original key as `rotated`, and create a new key pair with the required new key selector, reusing the same algorithm and key length. The rotated key remains valid for signing for a 3-day grace period, and is automatically changed to `retired` 2 weeks after rotation. Publish the new key to its DNS TXT record before rotated key expires for signing as emails sent with an unpublished key will fail DKIM validation by receiving providers. After the grace period, only the new key is valid for signing if published.
471
- * @param domain - The domain the DKIM key belongs to.
472
- * @param selector - The selector of the DKIM key to rotate.
473
- * @param options - The options to rotate the DKIM key.
474
- * @param options.newKey.selector - The selector for the new key pair. Must be a maximum of 63 characters.
475
- * @example
476
- * ```ts
477
- * const mailchannels = new MailChannels('your-api-key')
478
- * const { data, error } = await mailchannels.emails.rotateDkimKey('example.com', 'mailchannels', {
479
- * newKey: {
480
- * selector: 'new-selector'
481
- * }
482
- * })
483
- * ```
484
- */
485
- async rotateDkimKey(domain, selector, options) {
486
- let error = null;
487
- if (!selector || selector.length > 63) {
488
- error = createError("Selector must be between 1 and 63 characters.");
489
- return { data: null, error };
490
- }
491
- if (!options.newKey.selector || options.newKey.selector.length > 63) {
492
- error = createError("New key selector must be between 1 and 63 characters.");
493
- return { data: null, error };
494
- }
495
- const payload = {
496
- new_key: {
497
- selector: options.newKey.selector
498
- }
499
- };
500
- const response = await this.mailchannels.post(`/tx/v1/domains/${domain}/dkim-keys/${selector}/rotate`, {
501
- body: payload,
502
- onResponseError: async ({ response: response2 }) => {
503
- error = getStatusError(response2, {
504
- [ErrorCode.BadRequest]: "Bad Request.",
505
- [ErrorCode.NotFound]: "Specified key pair not found.",
506
- [ErrorCode.Conflict]: "Key pair already created for domain, and provided new key selector."
507
- });
508
- }
509
- }).catch((e) => {
510
- error ||= getResultError(e, "Failed to rotate DKIM key.");
511
- return null;
512
- });
513
- if (!response) return { data: null, error };
514
- const data = clean({
515
- new: {
516
- algorithm: response.new_key.algorithm,
517
- createdAt: response.new_key.created_at,
518
- dnsRecords: response.new_key.dkim_dns_records,
519
- domain: response.new_key.domain,
520
- gracePeriodExpiresAt: response.new_key.gracePeriodExpiresAt,
521
- length: response.new_key.key_length,
522
- publicKey: response.new_key.public_key,
523
- retiresAt: response.new_key.retiresAt,
524
- selector: response.new_key.selector,
525
- status: response.new_key.status,
526
- statusModifiedAt: response.new_key.status_modified_at
527
- },
528
- rotated: {
529
- algorithm: response.rotated_key.algorithm,
530
- createdAt: response.rotated_key.created_at,
531
- dnsRecords: response.rotated_key.dkim_dns_records,
532
- domain: response.rotated_key.domain,
533
- gracePeriodExpiresAt: response.rotated_key.gracePeriodExpiresAt,
534
- length: response.rotated_key.key_length,
535
- publicKey: response.rotated_key.public_key,
536
- retiresAt: response.rotated_key.retiresAt,
537
- selector: response.rotated_key.selector,
538
- status: response.rotated_key.status,
539
- statusModifiedAt: response.rotated_key.status_modified_at
540
- }
541
- });
542
- return { data, error: null };
543
- }
544
- }
545
-
546
- class Webhooks {
547
- constructor(mailchannels) {
548
- this.mailchannels = mailchannels;
549
- }
550
- /**
551
- * Enrolls the customer to receive event notifications via webhooks.
552
- * @param endpoint - The URL to receive event notifications. Must be no longer than `8000` characters.
553
- * @example
554
- * ```ts
555
- * const mailchannels = new MailChannels('your-api-key')
556
- * const { success, error } = mailchannels.webhooks.enroll('https://example.com/api/webhooks/mailchannels')
557
- * ```
558
- */
559
- async enroll(endpoint) {
560
- let error = null;
561
- if (!endpoint) {
562
- error = createError("No endpoint provided.");
563
- return { success: false, error };
564
- }
565
- if (endpoint.length > 8e3) {
566
- error = createError("The endpoint exceeds the maximum length of 8000 characters.");
567
- return { success: false, error };
568
- }
569
- await this.mailchannels.post("/tx/v1/webhook", {
570
- query: {
571
- endpoint
572
- },
573
- onResponseError: async ({ response }) => {
574
- error = getStatusError(response, {
575
- [ErrorCode.Conflict]: `Endpoint '${endpoint}' is already enrolled to receive notifications.`
576
- });
577
- }
578
- }).catch((e) => {
579
- error ||= getResultError(e, "Failed to enroll webhook.");
580
- });
581
- return { success: !error, error };
582
- }
583
- /**
584
- * Retrieves all registered webhook endpoints associated with the customer.
585
- * @example
586
- * ```ts
587
- * const mailchannels = new MailChannels('your-api-key')
588
- * const { data, error } = await mailchannels.webhooks.list()
589
- * ```
590
- */
591
- async list() {
592
- let error = null;
593
- const response = await this.mailchannels.get("/tx/v1/webhook", {
594
- onResponseError: async ({ response: response2 }) => {
595
- error = getStatusError(response2);
596
- }
597
- }).catch((e) => {
598
- error ||= getResultError(e, "Failed to fetch webhooks.");
599
- return null;
600
- });
601
- if (!response) return { data: null, error };
602
- const data = clean(response.map(({ webhook }) => webhook));
603
- return { data, error: null };
604
- }
605
- /**
606
- * Deletes all registered webhook endpoints for the customer.
607
- * @example
608
- * ```ts
609
- * const mailchannels = new MailChannels('your-api-key')
610
- * const { success, error } = await mailchannels.webhooks.delete()
611
- * ```
612
- */
613
- async delete() {
614
- let error = null;
615
- await this.mailchannels.delete("/tx/v1/webhook", {
616
- onResponseError: async ({ response }) => {
617
- error = getStatusError(response);
618
- }
619
- }).catch((e) => {
620
- error ||= getResultError(e, "Failed to delete webhooks.");
621
- });
622
- return { success: !error, error };
623
- }
624
- /**
625
- * Retrieves the public key used to verify signatures on incoming webhook payloads.
626
- * @param id - The ID of the key.
627
- * @example
628
- * ```ts
629
- * const mailchannels = new MailChannels('your-api-key')
630
- * const { data, error } = await mailchannels.webhooks.getSigningKey('key-id')
631
- * ```
632
- */
633
- async getSigningKey(id) {
634
- let error = null;
635
- const response = await this.mailchannels.get("/tx/v1/webhook/public-key", {
636
- query: {
637
- id
638
- },
639
- onResponseError: async ({ response: response2 }) => {
640
- error = getStatusError(response2, {
641
- [ErrorCode.BadRequest]: "Bad Request.",
642
- [ErrorCode.NotFound]: `The key '${id}' is not found.`
643
- });
644
- }
645
- }).catch((e) => {
646
- error ||= getResultError(e, "Failed to get signing key.");
647
- return null;
648
- });
649
- if (!response) return { data: null, error };
650
- const data = clean({ key: response.key });
651
- return { data, error: null };
652
- }
653
- /**
654
- * Validates whether your enrolled webhook(s) respond with an HTTP `2xx` status code. Sends a test request to each webhook containing your customer handle, a hardcoded event type (`test`), a hardcoded sender email (`test@mailchannels.com`), a timestamp, a request ID (provided or generated), and an SMTP ID. The response includes the HTTP status code and body returned by each webhook.
655
- * @param requestId - Optional identifier in the webhook payload. If not provided, a value will be automatically generated. Must not exceed 28 characters.
656
- * @example
657
- * ```ts
658
- * const mailchannels = new MailChannels('your-api-key')
659
- * const { data, error } = await mailchannels.webhooks.validate('optional-request-id')
660
- * ```
661
- */
662
- async validate(requestId) {
663
- let error = null;
664
- if (requestId && requestId.length > 28) {
665
- error = createError("The request id should not exceed 28 characters.");
666
- return { data: null, error };
667
- }
668
- const response = await this.mailchannels.post("/tx/v1/webhook/validate", {
669
- body: {
670
- request_id: requestId
671
- },
672
- onResponseError: async ({ response: response2 }) => {
673
- error = getStatusError(response2, {
674
- [ErrorCode.BadRequest]: "Bad Request.",
675
- [ErrorCode.NotFound]: "No webhooks found for the account."
676
- });
677
- }
678
- }).catch((e) => {
679
- error ||= getResultError(e, "Failed to validate webhooks.");
680
- return null;
681
- });
682
- if (!response) return { data: null, error };
683
- const data = clean({
684
- allPassed: response.all_passed,
685
- results: response.results
686
- });
687
- return { data, error: null };
688
- }
689
- }
690
-
691
- class SubAccounts {
692
- constructor(mailchannels) {
693
- this.mailchannels = mailchannels;
694
- }
695
- static COMPANY_PATTERN = /^.{3,128}$/;
696
- static HANDLE_PATTERN = /^[a-z0-9]{3,128}$/;
697
- /**
698
- * 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. Note that Sub-accounts are only available to parent accounts on 100K and higher plans.
699
- * @param companyName - The name of the company associated with the sub-account. This name is used for display purposes only and does not affect the functionality of the sub-account. The length must be between 3 and 128 characters.
700
- * @param handle - A unique name for the sub-account to be created. The length must be between 3 and 128 characters, and it may contain only lowercase letters and numbers. If not provided, a random handle will be generated.
701
- * @example
702
- * ```ts
703
- * const mailchannels = new MailChannels('your-api-key')
704
- * const { data, error } = await mailchannels.subAccounts.create('My Company', 'validhandle123')
705
- * ```
706
- */
707
- async create(companyName, handle) {
708
- let error = null;
709
- const isValidCompany = SubAccounts.COMPANY_PATTERN.test(companyName);
710
- if (!isValidCompany) {
711
- error = createError("Invalid company name. Company name must be between 3 and 128 characters.");
712
- return { data: null, error };
713
- }
714
- if (handle) {
715
- const isValidHandle = SubAccounts.HANDLE_PATTERN.test(handle);
716
- if (!isValidHandle) {
717
- error = createError("Invalid handle. Sub-account handle must be between 3 and 128 characters and contain only lowercase letters and numbers.");
718
- return { data: null, error };
719
- }
720
- }
721
- const response = await this.mailchannels.post("/tx/v1/sub-account", {
722
- body: {
723
- company_name: companyName,
724
- handle
725
- },
726
- onResponseError: async ({ response: response2 }) => {
727
- error = getStatusError(response2, {
728
- [ErrorCode.Forbidden]: "The parent account does not have permission to create sub-accounts.",
729
- [ErrorCode.Conflict]: `Sub-account with handle '${handle}' already exists.`
730
- });
731
- }
732
- }).catch((e) => {
733
- error ||= getResultError(e, "Failed to create sub-account.");
734
- return null;
735
- });
736
- if (!response) return { data: null, error };
737
- const data = clean({
738
- companyName: response.company_name,
739
- enabled: response.enabled,
740
- handle: response.handle
741
- });
742
- return { data, error: null };
743
- }
744
- /**
745
- * 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.
746
- * @param options - The options to filter the list of sub-accounts.
747
- * @example
748
- * ```ts
749
- * const mailchannels = new MailChannels('your-api-key')
750
- * const { data, error } = await mailchannels.subAccounts.list()
751
- * ```
752
- */
753
- async list(options) {
754
- let error = null;
755
- error = validateLimit(options?.limit, 1e3) || validateOffset(options?.offset);
756
- if (error) return { data: null, error };
757
- const response = await this.mailchannels.get("/tx/v1/sub-account", {
758
- query: options,
759
- onResponseError: async ({ response: response2 }) => {
760
- error = getStatusError(response2);
761
- }
762
- }).catch((e) => {
763
- error ||= getResultError(e, "Failed to fetch sub-accounts.");
764
- return null;
765
- });
766
- if (!response) return { data: null, error };
767
- const data = clean(response.map((account) => ({
768
- companyName: account.company_name,
769
- enabled: account.enabled,
770
- handle: account.handle
771
- })));
772
- return { data, error: null };
773
- }
774
- /**
775
- * Deletes the sub-account identified by its handle.
776
- * @param handle - Handle of sub-account to be deleted.
777
- * ```ts
778
- * const mailchannels = new MailChannels('your-api-key')
779
- * const { success, error } = await mailchannels.subAccounts.delete('validhandle123')
780
- * ```
781
- */
782
- async delete(handle) {
783
- let error = null;
784
- if (!handle) {
785
- error = createError("No handle provided.");
786
- return { success: false, error };
787
- }
788
- await this.mailchannels.delete(`/tx/v1/sub-account/${handle}`, {
789
- onResponseError: async ({ response }) => {
790
- error = getStatusError(response);
791
- }
792
- }).catch((e) => {
793
- error ||= getResultError(e, "Failed to delete sub-account.");
794
- });
795
- return { success: !error, error };
796
- }
797
- /**
798
- * Suspends the sub-account identified by its handle. This action disables the account, preventing it from sending any emails until it is reactivated.
799
- * @param handle - Handle of sub-account to be suspended.
800
- * @example
801
- * ```ts
802
- * const mailchannels = new MailChannels('your-api-key')
803
- * const { success, error } = await mailchannels.subAccounts.suspend('validhandle123')
804
- * ```
805
- */
806
- async suspend(handle) {
807
- let error = null;
808
- if (!handle) {
809
- error = createError("No handle provided.");
810
- return { success: false, error };
811
- }
812
- await this.mailchannels.post(`/tx/v1/sub-account/${handle}/suspend`, {
813
- onResponseError: async ({ response }) => {
814
- error = getStatusError(response, {
815
- [ErrorCode.NotFound]: `The specified sub-account '${handle}' does not exist.`
816
- });
817
- }
818
- }).catch((e) => {
819
- error ||= getResultError(e, "Failed to suspend sub-account.");
820
- });
821
- return { success: !error, error };
822
- }
823
- /**
824
- * Activates a suspended sub-account identified by its handle, restoring its ability to send emails.
825
- * @param handle - Handle of sub-account to be activated.
826
- * @example
827
- * ```ts
828
- * const mailchannels = new MailChannels('your-api-key')
829
- * const { success, error } = await mailchannels.subAccounts.activate('validhandle123')
830
- * ```
831
- */
832
- async activate(handle) {
833
- let error = null;
834
- if (!handle) {
835
- error = createError("No handle provided.");
836
- return { success: false, error };
837
- }
838
- await this.mailchannels.post(`/tx/v1/sub-account/${handle}/activate`, {
839
- onResponseError: async ({ response }) => {
840
- error = getStatusError(response, {
841
- [ErrorCode.Forbidden]: "The parent account does not have permission to activate the sub-account.",
842
- [ErrorCode.NotFound]: `The specified sub-account '${handle}' does not exist.`
843
- });
844
- }
845
- }).catch((e) => {
846
- error ||= getResultError(e, "Failed to activate sub-account.");
847
- });
848
- return { success: !error, error };
849
- }
850
- /**
851
- * Creates a new API key for the specified sub-account.
852
- * @param handle - Handle of the sub-account to create API key for.
853
- * @example
854
- * ```ts
855
- * const mailchannels = new MailChannels('your-api-key')
856
- * const { data, error } = await mailchannels.subAccounts.createApiKey('validhandle123')
857
- * ```
858
- */
859
- async createApiKey(handle) {
860
- let error = null;
861
- if (!handle) {
862
- error = createError("No handle provided.");
863
- return { data: null, error };
864
- }
865
- const response = await this.mailchannels.post(`/tx/v1/sub-account/${handle}/api-key`, {
866
- onResponseError: async ({ response: response2 }) => {
867
- error = getStatusError(response2, {
868
- [ErrorCode.Forbidden]: "You can't create API keys for this sub-account.",
869
- [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`,
870
- [ErrorCode.UnprocessableEntity]: "You have reached the limit of API keys you can create for this sub-account."
871
- });
872
- }
873
- }).catch((e) => {
874
- error ||= getResultError(e, "Failed to create sub-account API key.");
875
- return null;
876
- });
877
- if (!response) return { data: null, error };
878
- const data = clean({
879
- id: response.id,
880
- value: response.key
881
- });
882
- return { data, error: null };
883
- }
884
- /**
885
- * 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.
886
- * @param handle - Handle of the sub-account to retrieve the API key for.
887
- * @param options - The options to filter the list of API keys.
888
- * @example
889
- * ```ts
890
- * const mailchannels = new MailChannels('your-api-key')
891
- * const { data, error } = await mailchannels.subAccounts.listApiKeys('validhandle123')
892
- * ```
893
- */
894
- async listApiKeys(handle, options) {
895
- let error = null;
896
- if (!handle) {
897
- error = createError("No handle provided.");
898
- return { data: null, error };
899
- }
900
- error = validateLimit(options?.limit, 1e3) || validateOffset(options?.offset);
901
- if (error) return { data: null, error };
902
- const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/api-key`, {
903
- onResponseError: async ({ response: response2 }) => {
904
- error = getStatusError(response2, {
905
- [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
906
- });
907
- }
908
- }).catch((e) => {
909
- error ||= getResultError(e, "Failed to fetch sub-account API keys.");
910
- return null;
911
- });
912
- if (!response) return { data: null, error };
913
- const data = clean(response.map((key) => ({
914
- id: key.id,
915
- value: key.key
916
- })));
917
- return { data, error: null };
918
- }
919
- /**
920
- * Deletes the API key identified by its ID for the specified sub-account.
921
- * @param handle - Handle of the sub-account for which the API key should be deleted.
922
- * @param id - The ID of the API key to delete.
923
- * @example
924
- * ```ts
925
- * const mailchannels = new MailChannels('your-api-key')
926
- * const { success, error } = await mailchannels.subAccounts.deleteApiKey('validhandle123', 1)
927
- * ```
928
- */
929
- async deleteApiKey(handle, id) {
930
- let error = null;
931
- if (!handle) {
932
- error = createError("No handle provided.");
933
- return { success: false, error };
934
- }
935
- await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/api-key/${id}`, {
936
- onResponseError: async ({ response }) => {
937
- error = getStatusError(response, {
938
- [ErrorCode.BadRequest]: "Missing or invalid API key ID."
939
- });
940
- }
941
- }).catch((e) => {
942
- error ||= getResultError(e, "Failed to delete sub-account API key.");
943
- });
944
- return { success: !error, error };
945
- }
946
- /**
947
- * Creates a new SMTP password for the specified sub-account.
948
- * @param handle - Handle of the sub-account to create SMTP password for.
949
- * @example
950
- * ```ts
951
- * const mailchannels = new MailChannels('your-api-key')
952
- * const { data, error } = await mailchannels.subAccounts.createSmtpPassword('validhandle123')
953
- * ```
954
- */
955
- async createSmtpPassword(handle) {
956
- let error = null;
957
- if (!handle) {
958
- error = createError("No handle provided.");
959
- return { data: null, error };
960
- }
961
- const response = await this.mailchannels.post(`/tx/v1/sub-account/${handle}/smtp-password`, {
962
- onResponseError: async ({ response: response2 }) => {
963
- error = getStatusError(response2, {
964
- [ErrorCode.Forbidden]: "You can't create SMTP passwords for this sub-account.",
965
- [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`,
966
- [ErrorCode.UnprocessableEntity]: "You have reached the limit of SMTP passwords you can create for this sub-account."
967
- });
968
- }
969
- }).catch((e) => {
970
- error ||= getResultError(e, "Failed to create sub-account SMTP password.");
971
- return null;
972
- });
973
- if (!response) return { data: null, error };
974
- const data = clean({
975
- enabled: response.enabled,
976
- id: response.id,
977
- value: response.smtp_password
978
- });
979
- return { data, error: null };
980
- }
981
- /**
982
- * 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.
983
- * @param handle - Handle of the sub-account to retrieve the SMTP password for.
984
- * @example
985
- * ```ts
986
- * const mailchannels = new MailChannels('your-api-key')
987
- * const { data, error } = await mailchannels.subAccounts.listSmtpPasswords('validhandle123')
988
- * ```
989
- */
990
- async listSmtpPasswords(handle) {
991
- let error = null;
992
- if (!handle) {
993
- error = createError("No handle provided.");
994
- return { data: null, error };
995
- }
996
- const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/smtp-password`, {
997
- onResponseError: async ({ response: response2 }) => {
998
- error = getStatusError(response2, {
999
- [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
1000
- });
1001
- }
1002
- }).catch((e) => {
1003
- error ||= getResultError(e, "Failed to fetch sub-account SMTP passwords.");
1004
- return null;
1005
- });
1006
- if (!response) return { data: null, error };
1007
- const data = clean(response.map((password) => ({
1008
- enabled: password.enabled,
1009
- id: password.id,
1010
- value: password.smtp_password
1011
- })));
1012
- return { data, error: null };
1013
- }
1014
- /**
1015
- * Deletes the SMTP password identified by its ID for the specified sub-account.
1016
- * @param handle - Handle of the sub-account for which the SMTP password should be deleted.
1017
- * @param id - The ID of the SMTP password to delete.
1018
- * @example
1019
- * ```ts
1020
- * const mailchannels = new MailChannels('your-api-key')
1021
- * const { success, error } = await mailchannels.subAccounts.deleteSmtpPassword('validhandle123', 1)
1022
- * ```
1023
- */
1024
- async deleteSmtpPassword(handle, id) {
1025
- let error = null;
1026
- if (!handle) {
1027
- error = createError("No handle provided.");
1028
- return { success: false, error };
1029
- }
1030
- await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/smtp-password/${id}`, {
1031
- onResponseError: async ({ response }) => {
1032
- error = getStatusError(response, {
1033
- [ErrorCode.BadRequest]: "Missing or invalid SMTP password ID."
1034
- });
1035
- }
1036
- }).catch((e) => {
1037
- error ||= getResultError(e, "Failed to delete sub-account SMTP password.");
1038
- });
1039
- return { success: !error, error };
1040
- }
1041
- /**
1042
- * Retrieves the limit of a specified sub-account. A value of `-1` indicates that the sub-account inherits the parent account's limit, allowing the sub-account to utilize any remaining capacity within the parent account's allocation.
1043
- * @param handle - Handle of the sub-account to retrieve the limit for.
1044
- * @example
1045
- * ```ts
1046
- * const mailchannels = new MailChannels('your-api-key')
1047
- * const { data, error } = await mailchannels.subAccounts.getLimit('validhandle123')
1048
- * ```
1049
- */
1050
- async getLimit(handle) {
1051
- let error = null;
1052
- if (!handle) {
1053
- error = createError("No handle provided.");
1054
- return { data: null, error };
1055
- }
1056
- const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/limit`, {
1057
- onResponseError: async ({ response: response2 }) => {
1058
- error = getStatusError(response2, {
1059
- [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
1060
- });
1061
- }
1062
- }).catch((e) => {
1063
- error ||= getResultError(e, "Failed to fetch sub-account limit.");
1064
- return null;
1065
- });
1066
- if (!response) return { data: null, error };
1067
- const data = clean(response);
1068
- return { data, error: null };
1069
- }
1070
- /**
1071
- * Sets the limit for the specified sub-account.
1072
- * @param handle - Handle of the sub-account to set limit for.
1073
- * @param limit - The limits to set for the sub-account. The minimum allowed sends is `0`
1074
- * @example
1075
- * ```ts
1076
- * const mailchannels = new MailChannels('your-api-key')
1077
- * const { success, error } = await mailchannels.subAccounts.setLimit('validhandle123', { sends: 1000 })
1078
- * ```
1079
- */
1080
- async setLimit(handle, limit) {
1081
- let error = null;
1082
- if (!handle) {
1083
- error = createError("No handle provided.");
1084
- return { success: false, error };
1085
- }
1086
- await this.mailchannels.put(`/tx/v1/sub-account/${handle}/limit`, {
1087
- body: limit,
1088
- onResponseError: async ({ response }) => {
1089
- error = getStatusError(response, {
1090
- [ErrorCode.BadRequest]: "Bad Request.",
1091
- [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
1092
- });
1093
- }
1094
- }).catch((e) => {
1095
- error ||= getResultError(e, "Failed to set sub-account limit.");
1096
- });
1097
- return { success: !error, error };
1098
- }
1099
- /**
1100
- * Deletes the limit for the specified sub-account. After a successful deletion, the specified sub-account will be limited to the parent account's limit.
1101
- * @param handle - Handle of the sub-account to delete limit for.
1102
- * @example
1103
- * ```ts
1104
- * const mailchannels = new MailChannels('your-api-key')
1105
- * const { success, error } = await mailchannels.subAccounts.deleteLimit('validhandle123')
1106
- * ```
1107
- */
1108
- async deleteLimit(handle) {
1109
- let error = null;
1110
- if (!handle) {
1111
- error = createError("No handle provided.");
1112
- return { success: false, error };
1113
- }
1114
- await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/limit`, {
1115
- onResponseError: async ({ response }) => {
1116
- error = getStatusError(response, {
1117
- [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
1118
- });
1119
- }
1120
- }).catch((e) => {
1121
- error ||= getResultError(e, "Failed to delete sub-account limit.");
1122
- });
1123
- return { success: !error, error };
1124
- }
1125
- /**
1126
- * Retrieves usage statistics for the specified sub-account during the current billing period.
1127
- * @param handle - Handle of the sub-account to query usage stats for.
1128
- * @example
1129
- * ```ts
1130
- * const mailchannels = new MailChannels('your-api-key')
1131
- * const { data, error } = await mailchannels.subAccounts.getUsage('validhandle123')
1132
- * ```
1133
- */
1134
- async getUsage(handle) {
1135
- let error = null;
1136
- if (!handle) {
1137
- error = createError("No handle provided.");
1138
- return { data: null, error };
1139
- }
1140
- const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/usage`, {
1141
- onResponseError: async ({ response: response2 }) => {
1142
- error = getStatusError(response2, {
1143
- [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
1144
- });
1145
- }
1146
- }).catch((e) => {
1147
- error ||= getResultError(e, "Failed to fetch sub-account usage.");
1148
- return null;
1149
- });
1150
- if (!response) return { data: null, error };
1151
- const data = clean({
1152
- endDate: response.period_end_date,
1153
- startDate: response.period_start_date,
1154
- total: response.total_usage
1155
- });
1156
- return { data, error: null };
1157
- }
1158
- }
1159
-
1160
- class Metrics {
1161
- constructor(mailchannels) {
1162
- this.mailchannels = mailchannels;
1163
- }
1164
- /**
1165
- * Retrieve engagement metrics for messages sent from your account, including counts of open and click events. Supports optional filters for time range, and campaign ID.
1166
- * @param options - Options to filter and customize the engagement metrics retrieval.
1167
- * @example
1168
- * ```ts
1169
- * const mailchannels = new MailChannels('your-api-key')
1170
- * const { data, error } = await mailchannels.metrics.engagement()
1171
- * ```
1172
- */
1173
- async engagement(options) {
1174
- let error = null;
1175
- const response = await this.mailchannels.get("/tx/v1/metrics/engagement", {
1176
- query: {
1177
- start_time: options?.startTime,
1178
- end_time: options?.endTime,
1179
- campaign_id: options?.campaignId,
1180
- interval: options?.interval
1181
- },
1182
- onResponseError: async ({ response: response2 }) => {
1183
- error = getStatusError(response2, {
1184
- [ErrorCode.BadRequest]: "Bad Request."
1185
- });
1186
- }
1187
- }).catch((e) => {
1188
- error ||= getResultError(e, "Failed to fetch engagement metrics.");
1189
- return null;
1190
- });
1191
- if (!response) return { data: null, error };
1192
- const data = clean({
1193
- buckets: {
1194
- click: mapBuckets(response.buckets.click),
1195
- clickTrackingDelivered: mapBuckets(response.buckets.click_tracking_delivered),
1196
- open: mapBuckets(response.buckets.open),
1197
- openTrackingDelivered: mapBuckets(response.buckets.open_tracking_delivered)
1198
- },
1199
- click: response.click,
1200
- clickTrackingDelivered: response.click_tracking_delivered,
1201
- endTime: response.end_time,
1202
- open: response.open,
1203
- openTrackingDelivered: response.open_tracking_delivered,
1204
- startTime: response.start_time
1205
- });
1206
- return { data, error: null };
1207
- }
1208
- /**
1209
- * Retrieve performance metrics for messages sent from your account, including counts of processed, delivered, hard-bounced events. Supports optional filters for time range, and campaign ID.
1210
- * @param options - Options to filter and customize the performance metrics retrieval.
1211
- * @example
1212
- * ```ts
1213
- * const mailchannels = new MailChannels('your-api-key')
1214
- * const { data, error } = await mailchannels.metrics.performance()
1215
- * ```
1216
- */
1217
- async performance(options) {
1218
- let error = null;
1219
- const response = await this.mailchannels.get("/tx/v1/metrics/performance", {
1220
- query: {
1221
- start_time: options?.startTime,
1222
- end_time: options?.endTime,
1223
- campaign_id: options?.campaignId,
1224
- interval: options?.interval
1225
- },
1226
- onResponseError: async ({ response: response2 }) => {
1227
- error = getStatusError(response2, {
1228
- [ErrorCode.BadRequest]: "Bad Request."
1229
- });
1230
- }
1231
- }).catch((e) => {
1232
- error ||= getResultError(e, "Failed to fetch performance metrics.");
1233
- return null;
1234
- });
1235
- if (!response) return { data: null, error };
1236
- const data = clean({
1237
- bounced: response.bounced,
1238
- buckets: {
1239
- bounced: mapBuckets(response.buckets.bounced),
1240
- delivered: mapBuckets(response.buckets.delivered),
1241
- processed: mapBuckets(response.buckets.processed)
1242
- },
1243
- delivered: response.delivered,
1244
- endTime: response.end_time,
1245
- processed: response.processed,
1246
- startTime: response.start_time
1247
- });
1248
- return { data, error: null };
1249
- }
1250
- /**
1251
- * Retrieve recipient behaviour metrics for messages sent from your account, including counts of unsubscribed events. Supports optional filters for time range, and campaign ID.
1252
- * @param options - Options to filter and customize the recipient behaviour metrics retrieval.
1253
- * @example
1254
- * ```ts
1255
- * const mailchannels = new MailChannels('your-api-key')
1256
- * const { data, error } = await mailchannels.metrics.recipientBehaviour()
1257
- * ```
1258
- */
1259
- async recipientBehaviour(options) {
1260
- let error = null;
1261
- const response = await this.mailchannels.get("/tx/v1/metrics/recipient-behaviour", {
1262
- query: {
1263
- start_time: options?.startTime,
1264
- end_time: options?.endTime,
1265
- campaign_id: options?.campaignId,
1266
- interval: options?.interval
1267
- },
1268
- onResponseError: async ({ response: response2 }) => {
1269
- error = getStatusError(response2, {
1270
- [ErrorCode.BadRequest]: "Bad Request."
1271
- });
1272
- }
1273
- }).catch((e) => {
1274
- error ||= getResultError(e, "Failed to fetch recipient behaviour metrics.");
1275
- return null;
1276
- });
1277
- if (!response) return { data: null, error };
1278
- const data = clean({
1279
- buckets: {
1280
- unsubscribeDelivered: mapBuckets(response.buckets.unsubscribe_delivered),
1281
- unsubscribed: mapBuckets(response.buckets.unsubscribed)
1282
- },
1283
- endTime: response.end_time,
1284
- startTime: response.start_time,
1285
- unsubscribeDelivered: response.unsubscribe_delivered,
1286
- unsubscribed: response.unsubscribed
1287
- });
1288
- return { data, error: null };
1289
- }
1290
- /**
1291
- * Retrieve volume metrics for messages sent from your account, including counts of processed, delivered and dropped events. Supports optional filters for time range and campaign ID.
1292
- * @param options - Options to filter and customize the volume metrics retrieval.
1293
- * @example
1294
- * ```ts
1295
- * const mailchannels = new MailChannels('your-api-key')
1296
- * const { data, error } = await mailchannels.metrics.volume()
1297
- * ```
1298
- */
1299
- async volume(options) {
1300
- let error = null;
1301
- const response = await this.mailchannels.get("/tx/v1/metrics/volume", {
1302
- query: {
1303
- start_time: options?.startTime,
1304
- end_time: options?.endTime,
1305
- campaign_id: options?.campaignId,
1306
- interval: options?.interval
1307
- },
1308
- onResponseError: async ({ response: response2 }) => {
1309
- error = getStatusError(response2, {
1310
- [ErrorCode.BadRequest]: "Bad Request."
1311
- });
1312
- }
1313
- }).catch((e) => {
1314
- error ||= getResultError(e, "Failed to fetch volume metrics.");
1315
- return null;
1316
- });
1317
- if (!response) return { data: null, error };
1318
- const data = clean({
1319
- buckets: {
1320
- delivered: mapBuckets(response.buckets.delivered),
1321
- dropped: mapBuckets(response.buckets.dropped),
1322
- processed: mapBuckets(response.buckets.processed)
1323
- },
1324
- delivered: response.delivered,
1325
- dropped: response.dropped,
1326
- endTime: response.end_time,
1327
- processed: response.processed,
1328
- startTime: response.start_time
1329
- });
1330
- return { data, error: null };
1331
- }
1332
- /**
1333
- * Retrieves usage statistics during the current billing period.
1334
- * @example
1335
- * ```ts
1336
- * const mailchannels = new MailChannels('your-api-key')
1337
- * const { data, error } = await mailchannels.metrics.usage()
1338
- * ```
1339
- */
1340
- async usage() {
1341
- let error = null;
1342
- const response = await this.mailchannels.get("/tx/v1/usage", {
1343
- onResponseError: async ({ response: response2 }) => {
1344
- error = getStatusError(response2);
1345
- }
1346
- }).catch((e) => {
1347
- error ||= getResultError(e, "Failed to fetch usage metrics.");
1348
- return null;
1349
- });
1350
- if (!response) return { data: null, error };
1351
- const data = clean({
1352
- endDate: response.period_end_date,
1353
- startDate: response.period_start_date,
1354
- total: response.total_usage
1355
- });
1356
- return { data, error: null };
1357
- }
1358
- /**
1359
- * Retrieves a list of senders, either sub-accounts or campaigns, with their associated message metrics. Sorted by total # of sent messages (processed + dropped). Supports optional filter for time range, and optional settings for limit, offset, and sort order. Note: senders without any messages in the given time range will not be included in the results. The default time range is from one month ago to now, and the default sort order is descending.
1360
- * @param type - The type of senders to retrieve metrics for. Can be either `sub-accounts` or `campaigns`.
1361
- * @param options - Optional filter options for time range, limit, offset, and sort order.
1362
- * @example
1363
- * ```ts
1364
- * const mailchannels = new MailChannels('your-api-key')
1365
- * const { data, error } = await mailchannels.metrics.senders('campaigns')
1366
- * ```
1367
- */
1368
- async senders(type, options) {
1369
- let error = null;
1370
- error = validateLimit(options?.limit, 1e3) || validateOffset(options?.offset);
1371
- if (error) return { data: null, error };
1372
- const response = await this.mailchannels.get(`/tx/v1/metrics/senders/${type}`, {
1373
- query: {
1374
- start_time: options?.startTime,
1375
- end_time: options?.endTime,
1376
- limit: options?.limit,
1377
- offset: options?.offset,
1378
- sort_order: options?.sortOrder
1379
- },
1380
- onResponseError: async ({ response: response2 }) => {
1381
- error = getStatusError(response2, {
1382
- [ErrorCode.BadRequest]: "Bad Request."
1383
- });
1384
- }
1385
- }).catch((e) => {
1386
- error ||= getResultError(e, "Failed to fetch senders metrics.");
1387
- return null;
1388
- });
1389
- if (!response) return { data: null, error };
1390
- const data = clean({
1391
- endTime: response.end_time,
1392
- limit: response.limit,
1393
- offset: response.offset,
1394
- senders: response.senders,
1395
- startTime: response.start_time,
1396
- total: response.total
1397
- });
1398
- return { data, error: null };
1399
- }
1400
- }
1401
-
1402
- class Suppressions {
1403
- constructor(mailchannels) {
1404
- this.mailchannels = mailchannels;
1405
- }
1406
- /**
1407
- * Creates suppression entries for the specified account. Parent accounts can create suppression entries for all associated sub-accounts. If `types` is not provided, it defaults to `non-transactional`. The operation is atomic, meaning all entries are successfully added or none are added if an error occurs.
1408
- * @param options - The details of the suppression entries to create.
1409
- * @example
1410
- * ```ts
1411
- * const mailchannels = new MailChannels('your-api-key')
1412
- * const { success, error } = await mailchannels.suppressions.create({
1413
- * // ...
1414
- * });
1415
- */
1416
- async create(options) {
1417
- let error = null;
1418
- const { addToSubAccounts, entries } = options;
1419
- const payload = {
1420
- add_to_sub_accounts: addToSubAccounts,
1421
- suppression_entries: entries.map((entry) => ({
1422
- notes: entry.notes,
1423
- recipient: entry.recipient,
1424
- // Default to non-transactional when caller omits types
1425
- suppression_types: Array.from(new Set(entry.types || ["non-transactional"]))
1426
- }))
1427
- };
1428
- await this.mailchannels.post("/tx/v1/suppression-list", {
1429
- body: payload,
1430
- onResponseError: async ({ response }) => {
1431
- error = getStatusError(response, {
1432
- [ErrorCode.BadRequest]: "Bad Request.",
1433
- [ErrorCode.Conflict]: "Conflict. One or more suppression entries in the request already exist and cannot be created again.",
1434
- [ErrorCode.PayloadTooLarge]: "Payload too large. The request exceeds the maximum allowed total of 1000 suppression entries for the parent account and/or its sub-accounts."
1435
- });
1436
- }
1437
- }).catch((e) => {
1438
- error ||= getResultError(e, "Failed to create suppression entries.");
1439
- });
1440
- return { success: !error, error };
1441
- }
1442
- /**
1443
- * Deletes suppression entry associated with the account based on the specified recipient and source.
1444
- * @param recipient - The email address of the suppression entry to delete.
1445
- * @param source - The source of the suppression entry to be deleted. If source is not provided, it defaults to `api`. If source is set to `all`, all suppression entries related to the specified recipient will be deleted.
1446
- * @example
1447
- * ```ts
1448
- * const mailchannels = new MailChannels('your-api-key')
1449
- * const { success, error } = await mailchannels.suppressions.delete('name@example.com', 'api');
1450
- * ```
1451
- */
1452
- async delete(recipient, source) {
1453
- let error = null;
1454
- await this.mailchannels.delete(`/tx/v1/suppression-list/recipients/${recipient}`, {
1455
- query: {
1456
- source
1457
- },
1458
- onResponseError: async ({ response }) => {
1459
- error = getStatusError(response, {
1460
- [ErrorCode.BadRequest]: "Bad Request."
1461
- });
1462
- }
1463
- }).catch((e) => {
1464
- error ||= getResultError(e, "Failed to delete suppression entry.");
1465
- });
1466
- return { success: !error, error };
1467
- }
1468
- /**
1469
- * Retrieve suppression entries associated with the specified account. Supports filtering by recipient, source and creation date range. The response is paginated, with a default limit of `1000` entries per page and an offset of `0`.
1470
- * @example
1471
- * ```ts
1472
- * const mailchannels = new MailChannels('your-api-key')
1473
- * const { data, error } = await mailchannels.suppressions.list();
1474
- * ```
1475
- * @param options - Options to filter and customize the suppression entries retrieval.
1476
- */
1477
- async list(options) {
1478
- let error = null;
1479
- error = validateLimit(options?.limit, 1e3) || validateOffset(options?.offset);
1480
- if (error) return { data: null, error };
1481
- const payload = {
1482
- recipient: options?.recipient,
1483
- source: options?.source,
1484
- created_before: options?.createdBefore,
1485
- created_after: options?.createdAfter,
1486
- limit: options?.limit,
1487
- offset: options?.offset
1488
- };
1489
- const response = await this.mailchannels.get("/tx/v1/suppression-list", {
1490
- query: payload,
1491
- onResponseError: async ({ response: response2 }) => {
1492
- error = getStatusError(response2, {
1493
- [ErrorCode.BadRequest]: "Bad Request."
1494
- });
1495
- }
1496
- }).catch((e) => {
1497
- error ||= getResultError(e, "Failed to fetch suppression entries.");
1498
- return null;
1499
- });
1500
- if (!response) return { data: null, error };
1501
- const data = clean(response.suppression_list.map((entry) => ({
1502
- createdAt: entry.created_at,
1503
- notes: entry.notes,
1504
- recipient: entry.recipient,
1505
- sender: entry.sender,
1506
- source: entry.source,
1507
- types: entry.suppression_types
1508
- })));
1509
- return { data, error: null };
1510
- }
1511
- }
1512
-
1513
- class Domains {
1514
- constructor(mailchannels) {
1515
- this.mailchannels = mailchannels;
1516
- }
1517
- /**
1518
- * Provision a single domain to use MailChannels Inbound.
1519
- * @param options - The provision options and domain data.
1520
- * @example
1521
- * ```ts
1522
- * const mailchannels = new MailChannels('your-api-key')
1523
- * const { data, error } = await mailchannels.domains.provision({
1524
- * domain: 'example.com',
1525
- * subscriptionHandle: 'your-subscription-handle'
1526
- * })
1527
- * ```
1528
- */
1529
- async provision(options) {
1530
- let error = null;
1531
- const { associateKey, overwrite, ...payload } = options;
1532
- const response = await this.mailchannels.post("/inbound/v1/domains", {
1533
- query: {
1534
- "associate-key": associateKey,
1535
- "overwrite": overwrite
1536
- },
1537
- body: payload,
1538
- onResponseError: async ({ response: response2 }) => {
1539
- error = getStatusError(response2, {
1540
- [ErrorCode.BadRequest]: "Bad Request, returned in the case that an error occurs while converting an A-label domain to a U-label domain name.",
1541
- [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.",
1542
- [ErrorCode.Conflict]: `The domain '${options.domain}' is already provisioned, and is associated with a different customer.`
1543
- });
1544
- }
1545
- }).catch((e) => {
1546
- error ||= getResultError(e, "Failed to provision domain.");
1547
- return null;
1548
- });
1549
- if (!response) return { data: null, error };
1550
- const data = clean(response);
1551
- return { data, error: null };
1552
- }
1553
- /**
1554
- * Provision up to 1000 domains to use MailChannels Inbound.
1555
- * @param options - The options to provision the domains.
1556
- * @param domains - A list of domain data to provision.
1557
- * @example
1558
- * ```ts
1559
- * const mailchannels = new MailChannels('your-api-key')
1560
- * const { data, error } = await mailchannels.domains.bulkProvision({
1561
- * subscriptionHandle: 'your-subscription-handle'
1562
- * }, [
1563
- * {
1564
- * domain: 'example.com',
1565
- * admins: ['support@example.com']
1566
- * },
1567
- * {
1568
- * domain: 'example2.com'
1569
- * }
1570
- * ])
1571
- * ```
1572
- */
1573
- async bulkProvision(options, domains) {
1574
- let error = null;
1575
- const { associateKey, overwrite, subscriptionHandle } = options;
1576
- if (!domains || !domains.length) {
1577
- error = createError("No domains provided.");
1578
- return { data: null, error };
1579
- }
1580
- if (domains.length > 1e3) {
1581
- error = createError("The maximum number of domains to be provisioned is 1000.");
1582
- return { data: null, error };
1583
- }
1584
- const response = await this.mailchannels.post("/inbound/v1/domains/batch", {
1585
- query: {
1586
- subscriptionHandle,
1587
- "associate-key": associateKey,
1588
- "overwrite": overwrite
1589
- },
1590
- body: { domains },
1591
- onResponseError: async ({ response: response2 }) => {
1592
- error = getStatusError(response2, {
1593
- [ErrorCode.BadRequest]: "Bad Request, returned in the case that a domain name fails RFC 5891 validation.",
1594
- [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."
1595
- });
1596
- }
1597
- }).catch((e) => {
1598
- error ||= getResultError(e, "Failed to provision domains.");
1599
- return null;
1600
- });
1601
- if (!response) return { data: null, error };
1602
- const data = clean(response);
1603
- return { data, error: null };
1604
- }
1605
- /**
1606
- * Fetch a list of all domains associated with this API key.
1607
- * @param options - The options to filter the list of domains.
1608
- * @example
1609
- * ```ts
1610
- * const mailchannels = new MailChannels('your-api-key')
1611
- * const { data, error } = await mailchannels.domains.list()
1612
- * ```
1613
- */
1614
- async list(options) {
1615
- let error = null;
1616
- error = validateLimit(options?.limit, 5e3) || validateOffset(options?.offset);
1617
- if (error) return { data: null, error };
1618
- const response = await this.mailchannels.get("/inbound/v1/domains", {
1619
- query: options,
1620
- onResponseError: async ({ response: response2 }) => {
1621
- error = getStatusError(response2);
1622
- }
1623
- }).catch((e) => {
1624
- error ||= getResultError(e, "Failed to fetch domains.");
1625
- return null;
1626
- });
1627
- if (!response) return { data: null, error };
1628
- const data = clean({
1629
- domains: response.domains,
1630
- total: response.total
1631
- });
1632
- return { data, error: null };
1633
- }
1634
- /**
1635
- * De-provision a domain to cease protecting it with MailChannels Inbound.
1636
- * @param domain - The domain name to be removed.
1637
- * @example
1638
- * ```ts
1639
- * const mailchannels = new MailChannels('your-api-key')
1640
- * const { success, error } = await mailchannels.domains.delete('example.com')
1641
- * ```
1642
- */
1643
- async delete(domain) {
1644
- let error = null;
1645
- if (!domain) {
1646
- error = createError("No domain provided.");
1647
- return { success: false, error };
1648
- }
1649
- await this.mailchannels.delete(`/inbound/v1/domains/${domain}`, {
1650
- onResponseError: async ({ response }) => {
1651
- error = getStatusError(response, {
1652
- [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.",
1653
- [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1654
- });
1655
- }
1656
- }).catch((e) => {
1657
- error ||= getResultError(e, "Failed to delete domain.");
1658
- });
1659
- return { success: !error, error };
1660
- }
1661
- /**
1662
- * Add an entry to a domain blocklist or safelist.
1663
- * @param domain - The domain name.
1664
- * @param options - The options to add a list entry.
1665
- * @example
1666
- * ```ts
1667
- * const mailchannels = new MailChannels('your-api-key')
1668
- * const { data, error } = await mailchannels.domains.addListEntry('example.com', {
1669
- * listName: 'safelist',
1670
- * item: 'name@domain.com'
1671
- * })
1672
- * ```
1673
- */
1674
- async addListEntry(domain, options) {
1675
- const { listName, item } = options;
1676
- let error = null;
1677
- if (!domain) {
1678
- error = createError("No domain provided.");
1679
- return { data: null, error };
1680
- }
1681
- if (!listName) {
1682
- error = createError("No list name provided.");
1683
- return { data: null, error };
1684
- }
1685
- const response = await this.mailchannels.post(`/inbound/v1/domains/${domain}/lists/${listName}`, {
1686
- body: { item },
1687
- onResponseError: async ({ response: response2 }) => {
1688
- error = getStatusError(response2, {
1689
- [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.",
1690
- [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1691
- });
1692
- }
1693
- }).catch((e) => {
1694
- error ||= getResultError(e, "Failed to add domain list entry.");
1695
- return null;
1696
- });
1697
- if (!response) return { data: null, error };
1698
- const data = clean({
1699
- action: response.action,
1700
- item: response.item,
1701
- type: response.item_type
1702
- });
1703
- return { data, error: null };
1704
- }
1705
- /**
1706
- * Get domain list entries.
1707
- * @param domain - The domain name.
1708
- * @param listName - The name of the list to fetch. This can be a `blocklist`, `safelist`, `blacklist`, or `whitelist`.
1709
- * @example
1710
- * ```ts
1711
- * const mailchannels = new MailChannels('your-api-key')
1712
- * const { data, error } = await mailchannels.domains.listEntries('example.com', 'safelist')
1713
- * ```
1714
- */
1715
- async listEntries(domain, listName) {
1716
- let error = null;
1717
- if (!domain) {
1718
- error = createError("No domain provided.");
1719
- return { data: null, error };
1720
- }
1721
- if (!listName) {
1722
- error = createError("No list name provided.");
1723
- return { data: null, error };
1724
- }
1725
- const response = await this.mailchannels.get(`/inbound/v1/domains/${domain}/lists/${listName}`, {
1726
- onResponseError: async ({ response: response2 }) => {
1727
- error = getStatusError(response2, {
1728
- [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.",
1729
- [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1730
- });
1731
- }
1732
- }).catch((e) => {
1733
- error ||= getResultError(e, "Failed to fetch domain list entries.");
1734
- return null;
1735
- });
1736
- if (!response) return { data: null, error };
1737
- const data = clean(response.map(({ action, item, item_type }) => ({
1738
- action,
1739
- item,
1740
- type: item_type
1741
- })));
1742
- return { data, error: null };
1743
- }
1744
- /**
1745
- * Delete item from domain list.
1746
- * @param email - The domain name whose list will be modified.
1747
- * @param options - The options for the list entry to delete.
1748
- * @example
1749
- * ```ts
1750
- * const mailchannels = new MailChannels('your-api-key')
1751
- * const { success, error } = await mailchannels.domains.deleteListEntry('example.com', {
1752
- * listName: 'safelist',
1753
- * item: 'name@domain.com'
1754
- * })
1755
- * ```
1756
- */
1757
- async deleteListEntry(domain, options) {
1758
- const { listName, item } = options;
1759
- let error = null;
1760
- if (!domain) {
1761
- error = createError("No domain provided.");
1762
- return { success: false, error };
1763
- }
1764
- if (!listName) {
1765
- error = createError("No list name provided.");
1766
- return { success: false, error };
1767
- }
1768
- await this.mailchannels.delete(`/inbound/v1/domains/${domain}/lists/${listName}`, {
1769
- query: { item },
1770
- onResponseError: async ({ response }) => {
1771
- error = getStatusError(response, {
1772
- [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.",
1773
- [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1774
- });
1775
- }
1776
- }).catch((e) => {
1777
- error ||= getResultError(e, "Failed to delete domain list entry.");
1778
- });
1779
- return { success: !error, error };
1780
- }
1781
- /**
1782
- * Generate a link that allows a user to log in as a domain administrator.
1783
- * @param domain - The domain name.
1784
- * @example
1785
- * ```ts
1786
- * const mailchannels = new MailChannels('your-api-key')
1787
- * const { data, error } = await mailchannels.domains.createLoginLink('example.com')
1788
- * ```
1789
- */
1790
- async createLoginLink(domain) {
1791
- let error = null;
1792
- if (!domain) {
1793
- error = createError("No domain provided.");
1794
- return { data: null, error };
1795
- }
1796
- const response = await this.mailchannels.get(`/inbound/v1/domains/${domain}/login-link`, {
1797
- onResponseError: async ({ response: response2 }) => {
1798
- error = getStatusError(response2, {
1799
- [ErrorCode.Unauthorized]: "The domain does not belong to this customer.",
1800
- [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.",
1801
- [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1802
- });
1803
- }
1804
- }).catch((e) => {
1805
- error ||= getResultError(e, "Failed to create login link.");
1806
- return null;
1807
- });
1808
- if (!response) return { data: null, error };
1809
- const data = clean({
1810
- link: response.loginLink
1811
- });
1812
- return { data, error: null };
1813
- }
1814
- /**
1815
- * Sets the list of downstream addresses 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.
1816
- * @param domain - The domain name.
1817
- * @param records - The list of records to set for the domain. A maximum of 10 records can be set.
1818
- * @example
1819
- * ```ts
1820
- * const mailchannels = new MailChannels('your-api-key')
1821
- * const { success, error } = await mailchannels.domains.setDownstreamAddress('example.com', [
1822
- * {
1823
- * port: 25,
1824
- * priority: 10,
1825
- * target: 'example.com.',
1826
- * weight: 10
1827
- * }
1828
- * ])
1829
- * ```
1830
- */
1831
- async setDownstreamAddress(domain, records = []) {
1832
- let error = null;
1833
- if (!domain) {
1834
- error = createError("No domain provided.");
1835
- return { success: false, error };
1836
- }
1837
- if (records.length > 10) {
1838
- error = createError("The maximum of records to be set is 10.");
1839
- return { success: false, error };
1840
- }
1841
- await this.mailchannels.put(`/inbound/v1/domains/${domain}/downstream-address`, {
1842
- body: { records },
1843
- onResponseError: async ({ response }) => {
1844
- error = getStatusError(response, {
1845
- [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.",
1846
- [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1847
- });
1848
- }
1849
- }).catch((e) => {
1850
- error ||= getResultError(e, "Failed to set downstream address.");
1851
- });
1852
- return { success: !error, error };
1853
- }
1854
- /**
1855
- * Retrieve stored downstream addresses for the domain.
1856
- * @param domain - The domain name.
1857
- * @param options - The options to filter the list of downstream addresses.
1858
- * @example
1859
- * ```ts
1860
- * const mailchannels = new MailChannels('your-api-key')
1861
- * const { data, error } = await mailchannels.domains.listDownstreamAddresses('example.com')
1862
- * ```
1863
- */
1864
- async listDownstreamAddresses(domain, options) {
1865
- let error = null;
1866
- if (!domain) {
1867
- error = createError("No domain provided.");
1868
- return { data: null, error };
1869
- }
1870
- error = validateLimit(options?.limit) || validateOffset(options?.offset);
1871
- if (error) return { data: null, error };
1872
- const response = await this.mailchannels.get(`/inbound/v1/domains/${domain}/downstream-address`, {
1873
- query: options,
1874
- onResponseError: async ({ response: response2 }) => {
1875
- error = getStatusError(response2, {
1876
- [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.",
1877
- [ErrorCode.NotFound]: `The domain '${domain}' was not found.`
1878
- });
1879
- }
1880
- }).catch((e) => {
1881
- error ||= getResultError(e, "Failed to list downstream addresses.");
1882
- return null;
1883
- });
1884
- if (!response) return { data: null, error };
1885
- const data = clean(response.records);
1886
- return { data, error: null };
1887
- }
1888
- /**
1889
- * Update the API key that is associated with a domain.
1890
- * @param domain - The domain name.
1891
- * @param key - The new API key to associate with this domain.
1892
- * @example
1893
- * ```ts
1894
- * const mailchannels = new MailChannels('your-api-key')
1895
- * const { success, error } = await mailchannels.domains.updateApiKey('example.com', 'your-api-key')
1896
- * ```
1897
- */
1898
- async updateApiKey(domain, key) {
1899
- let error = null;
1900
- if (!domain) {
1901
- error = createError("No domain provided.");
1902
- return { success: false, error };
1903
- }
1904
- if (!key) {
1905
- error = createError("No API key provided.");
1906
- return { success: false, error };
1907
- }
1908
- await this.mailchannels.put(`/inbound/v1/domains/${domain}/api-key`, {
1909
- body: { apiKey: key },
1910
- onResponseError: async ({ response }) => {
1911
- error = getStatusError(response, {
1912
- [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.",
1913
- [ErrorCode.NotFound]: "The domain does not exist."
1914
- });
1915
- }
1916
- }).catch((e) => {
1917
- error ||= getResultError(e, "Failed to update domain API key.");
1918
- });
1919
- return { success: !error, error };
1920
- }
1921
- /**
1922
- * Generate a batch of links that allow a user to log in as a domain administrator to their different domains.
1923
- * @param domains - The list of domain names. Maximum of `1000` links per request.
1924
- * @example
1925
- * ```ts
1926
- * const mailchannels = new MailChannels('your-api-key')
1927
- * const { data, error } = await mailchannels.domains.bulkCreateLoginLinks(['example.com', 'example2.com'])
1928
- * ```
1929
- */
1930
- async bulkCreateLoginLinks(domains) {
1931
- let error = null;
1932
- if (!domains || !domains.length) {
1933
- error = createError("No domains provided.");
1934
- return { data: null, error };
1935
- }
1936
- if (domains.length > 1e3) {
1937
- error = createError("The maximum number of domains to create login links for is 1000.");
1938
- return { data: null, error };
1939
- }
1940
- const response = await this.mailchannels.post("/inbound/v1/domains/batch/login-link", {
1941
- body: {
1942
- domains: domains.map((domain) => ({ domain }))
1943
- },
1944
- onResponseError: async ({ response: response2 }) => {
1945
- error = getStatusError(response2, {
1946
- [ErrorCode.BadRequest]: "Bad Request."
1947
- });
1948
- }
1949
- }).catch((e) => {
1950
- error ||= getResultError(e, "Failed to create login links.");
1951
- return null;
1952
- });
1953
- if (!response) return { data: null, error };
1954
- const data = clean(response);
1955
- return { data, error: null };
1956
- }
1957
- }
1958
-
1959
- class Lists {
1960
- constructor(mailchannels) {
1961
- this.mailchannels = mailchannels;
1962
- }
1963
- /**
1964
- * Add item to account-level list
1965
- * @param options - The options for the list entry to add.
1966
- * @example
1967
- * ```ts
1968
- * const mailchannels = new MailChannels('your-api-key')
1969
- * const { data, error } = await mailchannels.lists.addListEntry({
1970
- * listName: 'safelist',
1971
- * item: 'name@domain.com'
1972
- * })
1973
- * ```
1974
- */
1975
- async addListEntry(options) {
1976
- let error = null;
1977
- const { listName, item } = options;
1978
- if (!listName) {
1979
- error = createError("No list name provided.");
1980
- return { data: null, error };
1981
- }
1982
- const response = await this.mailchannels.post(`/inbound/v1/lists/${listName}`, {
1983
- body: { item },
1984
- onResponseError: async ({ response: response2 }) => {
1985
- error = getStatusError(response2);
1986
- }
1987
- }).catch((e) => {
1988
- error ||= getResultError(e, "Failed to add list entry.");
1989
- return null;
1990
- });
1991
- if (!response) return { data: null, error };
1992
- const data = clean({
1993
- action: response.action,
1994
- item: response.item,
1995
- type: response.item_type
1996
- });
1997
- return { data, error: null };
1998
- }
1999
- /**
2000
- * Get account-level list entries.
2001
- * @param listName - The name of the list to fetch. This can be a `blocklist`, `safelist`, `blacklist`, or `whitelist`.
2002
- * @example
2003
- * ```ts
2004
- * const mailchannels = new MailChannels('your-api-key')
2005
- * const { data, error } = await mailchannels.lists.listEntries('safelist')
2006
- * ```
2007
- */
2008
- async listEntries(listName) {
2009
- let error = null;
2010
- if (!listName) {
2011
- error = createError("No list name provided.");
2012
- return { data: null, error };
2013
- }
2014
- const response = await this.mailchannels.get(`/inbound/v1/lists/${listName}`, {
2015
- onResponseError: async ({ response: response2 }) => {
2016
- error = getStatusError(response2);
2017
- }
2018
- }).catch((e) => {
2019
- error ||= getResultError(e, "Failed to fetch list entries.");
2020
- return null;
2021
- });
2022
- if (!response) return { data: null, error };
2023
- const data = clean(response.map(({ action, item, item_type }) => ({
2024
- action,
2025
- item,
2026
- type: item_type
2027
- })));
2028
- return { data, error: null };
2029
- }
2030
- /**
2031
- * Delete item from account-level list.
2032
- * @param options - The options for the list entry to delete.
2033
- * @example
2034
- * ```ts
2035
- * const mailchannels = new MailChannels('your-api-key')
2036
- * const { success, error } = await mailchannels.lists.deleteListEntry({
2037
- * listName: 'safelist',
2038
- * item: 'name@domain.com'
2039
- * })
2040
- * ```
2041
- */
2042
- async deleteListEntry(options) {
2043
- const { listName, item } = options;
2044
- let error = null;
2045
- if (!listName) {
2046
- error = createError("No list name provided.");
2047
- return { success: false, error };
2048
- }
2049
- await this.mailchannels.delete(`/inbound/v1/lists/${listName}`, {
2050
- query: { item },
2051
- onResponseError: async ({ response }) => {
2052
- error = getStatusError(response);
2053
- }
2054
- }).catch((e) => {
2055
- error ||= getResultError(e, "Failed to delete list entry.");
2056
- });
2057
- return { success: !error, error };
2058
- }
2059
- }
2060
-
2061
- class Users {
2062
- constructor(mailchannels) {
2063
- this.mailchannels = mailchannels;
2064
- }
2065
- /**
2066
- * Create a recipient user.
2067
- * @param email - The email address of the user to create.
2068
- * @param options - The options for the user to create.
2069
- * @example
2070
- * ```ts
2071
- * const mailchannels = new MailChannels('your-api-key')
2072
- * const { data, error } = await mailchannels.users.create("name@example.com", {
2073
- * admin: true
2074
- * })
2075
- * ```
2076
- */
2077
- async create(email, options) {
2078
- const { admin, filter, listEntries } = options || {};
2079
- let error = null;
2080
- if (!email) {
2081
- error = createError("No email address provided.");
2082
- return { data: null, error };
2083
- }
2084
- const response = await this.mailchannels.put("/inbound/v1/users", {
2085
- query: {
2086
- email_address: email,
2087
- admin: Boolean(admin),
2088
- filter
2089
- },
2090
- body: {
2091
- list_entries: listEntries
2092
- },
2093
- onResponseError: async ({ response: response2 }) => {
2094
- error = getStatusError(response2, {
2095
- [ErrorCode.BadRequest]: `The email address '${email}' is invalid.`
2096
- });
2097
- }
2098
- }).catch((e) => {
2099
- error ||= getResultError(e, "Failed to create user.");
2100
- return null;
2101
- });
2102
- if (!response) return { data: null, error };
2103
- const data = clean({
2104
- email: response.recipient.email_address,
2105
- roles: response.recipient.roles,
2106
- filter: response.recipient.filter,
2107
- listEntries: response.list_entries.map(({ item, item_type, action }) => ({
2108
- item,
2109
- type: item_type,
2110
- action
2111
- }))
2112
- });
2113
- return { data, error: null };
2114
- }
2115
- /**
2116
- * Add item to recipient user list
2117
- * @param email - The email address of the recipient whose list will be modified.
2118
- * @param options - The options for the list entry to add.
2119
- * @example
2120
- * ```ts
2121
- * const mailchannels = new MailChannels('your-api-key')
2122
- * const { data, error } = await mailchannels.users.addListEntry('name@example.com', {
2123
- * listName: 'safelist',
2124
- * item: 'name@domain.com'
2125
- * })
2126
- * ```
2127
- */
2128
- async addListEntry(email, options) {
2129
- const { listName, item } = options;
2130
- let error = null;
2131
- if (!email) {
2132
- error = createError("No email provided.");
2133
- return { data: null, error };
2134
- }
2135
- if (!listName) {
2136
- error = createError("No list name provided.");
2137
- return { data: null, error };
2138
- }
2139
- const response = await this.mailchannels.post(`/inbound/v1/users/${email}/lists/${listName}`, {
2140
- body: { item },
2141
- onResponseError: async ({ response: response2 }) => {
2142
- error = getStatusError(response2, {
2143
- [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.",
2144
- [ErrorCode.NotFound]: `The recipient '${email}' was not found.`
2145
- });
2146
- }
2147
- }).catch((e) => {
2148
- error ||= getResultError(e, "Failed to add user list entry.");
2149
- return null;
2150
- });
2151
- if (!response) return { data: null, error };
2152
- const data = clean({
2153
- action: response.action,
2154
- item: response.item,
2155
- type: response.item_type
2156
- });
2157
- return { data, error: null };
2158
- }
2159
- /**
2160
- * Get recipient list entries.
2161
- * @param email - The email address of the recipient whose list will be fetched.
2162
- * @param listName - The name of the list to fetch. This can be a `blocklist`, `safelist`, `blacklist`, or `whitelist`.
2163
- * @example
2164
- * ```ts
2165
- * const mailchannels = new MailChannels('your-api-key')
2166
- * const { data, error } = await mailchannels.users.listEntries('name@example.com', 'safelist')
2167
- * ```
2168
- */
2169
- async listEntries(email, listName) {
2170
- let error = null;
2171
- if (!email) {
2172
- error = createError("No email provided.");
2173
- return { data: null, error };
2174
- }
2175
- if (!listName) {
2176
- error = createError("No list name provided.");
2177
- return { data: null, error };
2178
- }
2179
- const response = await this.mailchannels.get(`/inbound/v1/users/${email}/lists/${listName}`, {
2180
- onResponseError: async ({ response: response2 }) => {
2181
- error = getStatusError(response2, {
2182
- [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.",
2183
- [ErrorCode.NotFound]: `The recipient '${email}' was not found.`
2184
- });
2185
- }
2186
- }).catch((e) => {
2187
- error ||= getResultError(e, "Failed to fetch user list entries.");
2188
- return null;
2189
- });
2190
- if (!response) return { data: null, error };
2191
- const data = clean(response.map(({ action, item, item_type }) => ({
2192
- action,
2193
- item,
2194
- type: item_type
2195
- })));
2196
- return { data, error: null };
2197
- }
2198
- /**
2199
- * Delete item from recipient list.
2200
- * @param email - The email address of the recipient whose list will be modified.
2201
- * @param options - The options for the list entry to delete.
2202
- * @example
2203
- * ```ts
2204
- * const mailchannels = new MailChannels('your-api-key')
2205
- * const { success, error } = await mailchannels.users.deleteListEntry('name@example.com', {
2206
- * listName: 'safelist',
2207
- * item: 'name@domain.com'
2208
- * })
2209
- * ```
2210
- */
2211
- async deleteListEntry(email, options) {
2212
- const { listName, item } = options;
2213
- let error = null;
2214
- if (!email) {
2215
- error = createError("No email provided.");
2216
- return { success: false, error };
2217
- }
2218
- if (!listName) {
2219
- error = createError("No list name provided.");
2220
- return { success: false, error };
2221
- }
2222
- await this.mailchannels.delete(`/inbound/v1/users/${email}/lists/${listName}`, {
2223
- query: { item },
2224
- onResponseError: async ({ response }) => {
2225
- error = getStatusError(response, {
2226
- [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.",
2227
- [ErrorCode.NotFound]: `The recipient '${email}' was not found.`
2228
- });
2229
- }
2230
- }).catch((e) => {
2231
- error ||= getResultError(e, "Failed to delete user list entry.");
2232
- });
2233
- return { success: !error, error };
2234
- }
2235
- }
2236
-
2237
- class Service {
2238
- constructor(mailchannels) {
2239
- this.mailchannels = mailchannels;
2240
- }
2241
- /**
2242
- * Retrieve the condition of the service
2243
- * @example
2244
- * ```ts
2245
- * const mailchannels = new MailChannels('your-api-key')
2246
- * const { success, error } = await mailchannels.service.status()
2247
- * ```
2248
- */
2249
- async status() {
2250
- let error = null;
2251
- await this.mailchannels.get("/inbound/v1/status", {
2252
- onResponseError: async ({ response }) => {
2253
- error = getStatusError(response);
2254
- }
2255
- }).catch((e) => {
2256
- error ||= getResultError(e, "Failed to fetch service status.");
2257
- });
2258
- return { success: !error, error };
2259
- }
2260
- /**
2261
- * Get a list of your subscriptions to MailChannels Inbound
2262
- * @example
2263
- * ```ts
2264
- * const mailchannels = new MailChannels('your-api-key')
2265
- * const { data, error } = await mailchannels.service.subscriptions()
2266
- * ```
2267
- */
2268
- async subscriptions() {
2269
- let error = null;
2270
- const response = await this.mailchannels.get("/inbound/v1/subscriptions", {
2271
- onResponseError: async ({ response: response2 }) => {
2272
- error = getStatusError(response2, {
2273
- [ErrorCode.NotFound]: "We could not find a customer that matched the customerHandle."
2274
- });
2275
- }
2276
- }).catch((e) => {
2277
- error ||= getResultError(e, "Failed to fetch subscriptions.");
2278
- return null;
2279
- });
2280
- if (!response) return { data: null, error };
2281
- const data = clean(response);
2282
- return { data, error: null };
2283
- }
2284
- /**
2285
- * Submit a false negative or false positive report.
2286
- * @param options - The report options
2287
- * @example
2288
- * ```ts
2289
- * const mailchannels = new MailChannels('your-api-key')
2290
- * const { success, error } = await mailchannels.service.report({
2291
- * // ...
2292
- * })
2293
- * ```
2294
- */
2295
- async report(options) {
2296
- let error = null;
2297
- const { type, ...payload } = options;
2298
- await this.mailchannels.post("/inbound/v1/report", {
2299
- query: {
2300
- report_type: type
2301
- },
2302
- body: payload,
2303
- onResponseError: async ({ response }) => {
2304
- error = getStatusError(response);
2305
- }
2306
- }).catch((e) => {
2307
- error ||= getResultError(e, "Failed to submit report.");
2308
- });
2309
- return { success: !error, error };
2310
- }
2311
- }
2312
-
2313
- class MailChannels extends MailChannelsClient {
2314
- // Modules: Email API
2315
- emails = new Emails(this);
2316
- webhooks = new Webhooks(this);
2317
- subAccounts = new SubAccounts(this);
2318
- metrics = new Metrics(this);
2319
- suppressions = new Suppressions(this);
2320
- // Modules: Inbound API
2321
- domains = new Domains(this);
2322
- lists = new Lists(this);
2323
- users = new Users(this);
2324
- service = new Service(this);
2325
- constructor(key) {
2326
- super(key);
2327
- }
2328
- }
2329
-
2330
2058
  export { Domains, Emails, Lists, MailChannels, MailChannelsClient, Metrics, Service, SubAccounts, Suppressions, Users, Webhooks };