mailchannels-sdk 0.4.7 → 0.5.0

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