mailchannels-sdk 0.3.7 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/modules.mjs CHANGED
@@ -9,7 +9,16 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
9
9
  return ErrorCode2;
10
10
  })(ErrorCode || {});
11
11
  const getStatusError = (response, errors = {}) => {
12
- return errors[response.status] || response._data?.message || "Unknown error.";
12
+ const statusText = errors[response.status] || "Unknown error.";
13
+ let details = "";
14
+ if (typeof response._data === "string") {
15
+ details = response._data;
16
+ } else if (response._data?.message) {
17
+ details = response._data.message;
18
+ } else if (Array.isArray(response._data?.errors) && response._data.errors.length) {
19
+ details = response._data.errors.join(", ");
20
+ }
21
+ return details ? `${statusText} ${details}` : statusText;
13
22
  };
14
23
 
15
24
  const parseRecipientString = (input) => {
@@ -84,6 +93,7 @@ class Emails {
84
93
  if (html) content.push({ type: "text/html", value: html, template_type });
85
94
  const payload = {
86
95
  attachments: options.attachments,
96
+ campaign_id: options.campaignId,
87
97
  personalizations: [{
88
98
  bcc: parseArrayRecipients(bcc),
89
99
  cc: parseArrayRecipients(cc),
@@ -100,7 +110,8 @@ class Emails {
100
110
  tracking_settings: options.tracking ? {
101
111
  click_tracking: options.tracking.click ? { enable: options.tracking.click } : void 0,
102
112
  open_tracking: options.tracking.open ? { enable: options.tracking.open } : void 0
103
- } : void 0
113
+ } : void 0,
114
+ transactional: options.transactional
104
115
  };
105
116
  const response = await this.mailchannels.post("/tx/v1/send", {
106
117
  query: { "dry-run": dryRun },
@@ -181,7 +192,7 @@ class Webhooks {
181
192
  }
182
193
  /**
183
194
  * Enrolls the customer to receive event notifications via webhooks.
184
- * @param endpoint - The URL to receive event notifications.
195
+ * @param endpoint - The URL to receive event notifications. Must be no longer than `8000` characters.
185
196
  * @example
186
197
  * ```ts
187
198
  * const mailchannels = new MailChannels('your-api-key')
@@ -194,6 +205,10 @@ class Webhooks {
194
205
  data.error = "No endpoint provided.";
195
206
  return data;
196
207
  }
208
+ if (endpoint.length > 8e3) {
209
+ data.error = "The endpoint exceeds the maximum length of 8000 characters.";
210
+ return data;
211
+ }
197
212
  await this.mailchannels.post("/tx/v1/webhook", {
198
213
  query: {
199
214
  endpoint
@@ -276,33 +291,75 @@ class Webhooks {
276
291
  data.key = response?.key || null;
277
292
  return data;
278
293
  }
294
+ /**
295
+ * 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.
296
+ * @param requestId - Optional identifier in the webhook payload. If not provided, a value will be automatically generated. Must not exceed 28 characters.
297
+ * @example
298
+ * ```ts
299
+ * const mailchannels = new MailChannels('your-api-key')
300
+ * const { allPassed, results } = await mailchannels.webhooks.validate('optional-request-id')
301
+ * ```
302
+ */
303
+ async validate(requestId) {
304
+ const data = { allPassed: false, results: [], error: null };
305
+ if (requestId && requestId.length > 28) {
306
+ data.error = "The request id should not exceed 28 characters.";
307
+ return data;
308
+ }
309
+ const response = await this.mailchannels.post("/tx/v1/webhook/validate", {
310
+ body: {
311
+ request_id: requestId
312
+ },
313
+ onResponseError: ({ response: response2 }) => {
314
+ data.error = getStatusError(response2, {
315
+ [ErrorCode.BadRequest]: "Bad Request.",
316
+ [ErrorCode.NotFound]: "No webhooks found for the account."
317
+ });
318
+ }
319
+ }).catch(() => null);
320
+ if (response) {
321
+ data.allPassed = response.all_passed;
322
+ data.results = response.results;
323
+ }
324
+ return data;
325
+ }
279
326
  }
280
327
 
281
328
  class SubAccounts {
282
329
  constructor(mailchannels) {
283
330
  this.mailchannels = mailchannels;
284
331
  }
332
+ static COMPANY_PATTERN = /^.{3,128}$/;
285
333
  static HANDLE_PATTERN = /^[a-z0-9]{3,128}$/;
286
334
  /**
287
- * Creates a new sub-account under the parent account. Each sub-account must have a unique handle composed solely of lowercase alphanumeric characters. If no handle is provided, a random handle will be generated.
288
- * @param handle - The handle of the sub-account to create. Sub-account handle must match the pattern `[a-z0-9]{3,128}`.
335
+ * 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.
336
+ * @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.
337
+ * @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.
289
338
  * @example
290
339
  * ```ts
291
340
  * const mailchannels = new MailChannels('your-api-key')
292
- * const { account } = await mailchannels.subAccounts.create('validhandle123')
341
+ * const { account } = await mailchannels.subAccounts.create('My Company', 'validhandle123')
293
342
  * ```
294
343
  */
295
- async create(handle) {
344
+ async create(companyName, handle) {
296
345
  const data = { account: null, error: null };
346
+ const isValidCompany = SubAccounts.COMPANY_PATTERN.test(companyName);
347
+ if (!isValidCompany) {
348
+ data.error = "Invalid company name. Company name must be between 3 and 128 characters.";
349
+ return data;
350
+ }
297
351
  if (handle) {
298
352
  const isValidHandle = SubAccounts.HANDLE_PATTERN.test(handle);
299
353
  if (!isValidHandle) {
300
- data.error = "Invalid handle. Sub-account handle must match the pattern [a-z0-9]{3,128}";
354
+ data.error = "Invalid handle. Sub-account handle must be between 3 and 128 characters and contain only lowercase letters and numbers.";
301
355
  return data;
302
356
  }
303
357
  }
304
358
  const response = await this.mailchannels.post("/tx/v1/sub-account", {
305
- body: handle ? { handle } : void 0,
359
+ body: {
360
+ company_name: companyName,
361
+ handle
362
+ },
306
363
  onResponseError: ({ response: response2 }) => {
307
364
  data.error = getStatusError(response2, {
308
365
  [ErrorCode.Forbidden]: "The parent account does not have permission to create sub-accounts.",
@@ -310,7 +367,12 @@ class SubAccounts {
310
367
  });
311
368
  }
312
369
  }).catch(() => null);
313
- data.account = response;
370
+ if (!response) return data;
371
+ data.account = {
372
+ companyName: response.company_name,
373
+ enabled: response.enabled,
374
+ handle: response.handle
375
+ };
314
376
  return data;
315
377
  }
316
378
  /**
@@ -338,7 +400,11 @@ class SubAccounts {
338
400
  data.error = getStatusError(response2);
339
401
  }
340
402
  }).catch(() => []);
341
- data.accounts = response;
403
+ data.accounts = response.map((account) => ({
404
+ companyName: account.company_name,
405
+ enabled: account.enabled,
406
+ handle: account.handle
407
+ }));
342
408
  return data;
343
409
  }
344
410
  /**
@@ -606,76 +672,430 @@ class SubAccounts {
606
672
  });
607
673
  return data;
608
674
  }
609
- }
610
-
611
- class Service {
612
- constructor(mailchannels) {
613
- this.mailchannels = mailchannels;
675
+ /**
676
+ * 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.
677
+ * @param handle - Handle of the sub-account to retrieve the limit for.
678
+ * @example
679
+ * ```ts
680
+ * const mailchannels = new MailChannels('your-api-key')
681
+ * const { limit } = await mailchannels.subAccounts.getLimit('validhandle123')
682
+ * ```
683
+ */
684
+ async getLimit(handle) {
685
+ const data = { limit: null, error: null };
686
+ if (!handle) {
687
+ data.error = "No handle provided.";
688
+ return data;
689
+ }
690
+ const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/limit`, {
691
+ onResponseError: async ({ response: response2 }) => {
692
+ data.error = getStatusError(response2, {
693
+ [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
694
+ });
695
+ }
696
+ }).catch(() => null);
697
+ if (!response) return data;
698
+ data.limit = response;
699
+ return data;
614
700
  }
615
701
  /**
616
- * Retrieve the condition of the service
702
+ * Sets the limit for the specified sub-account.
703
+ * @param handle - Handle of the sub-account to set limit for.
704
+ * @param limit - The limits to set for the sub-account. The minimum allowed sends is `0`
617
705
  * @example
618
706
  * ```ts
619
707
  * const mailchannels = new MailChannels('your-api-key')
620
- * const { success } = await mailchannels.service.status()
708
+ * const { success } = await mailchannels.subAccounts.setLimit('validhandle123', { sends: 1000 })
621
709
  * ```
622
710
  */
623
- async status() {
711
+ async setLimit(handle, limit) {
624
712
  const data = { success: false, error: null };
625
- await this.mailchannels.get("/inbound/v1/status", {
713
+ if (!handle) {
714
+ data.error = "No handle provided.";
715
+ return data;
716
+ }
717
+ await this.mailchannels.put(`/tx/v1/sub-account/${handle}/limit`, {
718
+ body: limit,
626
719
  ignoreResponseError: true,
627
720
  onResponse: async ({ response }) => {
628
721
  if (response.ok) {
629
722
  data.success = true;
630
723
  return;
631
724
  }
632
- data.error = getStatusError(response);
725
+ data.error = getStatusError(response, {
726
+ [ErrorCode.BadRequest]: "Bad Request.",
727
+ [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
728
+ });
633
729
  }
634
730
  });
635
731
  return data;
636
732
  }
637
733
  /**
638
- * Get a list of your subscriptions to MailChannels Inbound
734
+ * 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.
735
+ * @param handle - Handle of the sub-account to delete limit for.
639
736
  * @example
640
737
  * ```ts
641
738
  * const mailchannels = new MailChannels('your-api-key')
642
- * const { subscriptions } = await mailchannels.service.subscriptions()
739
+ * const { success } = await mailchannels.subAccounts.deleteLimit('validhandle123')
643
740
  * ```
644
741
  */
645
- async subscriptions() {
646
- const data = { subscriptions: [], error: null };
647
- const response = await this.mailchannels.get("/inbound/v1/subscriptions", {
742
+ async deleteLimit(handle) {
743
+ const data = { success: false, error: null };
744
+ if (!handle) {
745
+ data.error = "No handle provided.";
746
+ return data;
747
+ }
748
+ await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/limit`, {
749
+ ignoreResponseError: true,
750
+ onResponse: async ({ response }) => {
751
+ if (response.ok) {
752
+ data.success = true;
753
+ return;
754
+ }
755
+ data.error = getStatusError(response, {
756
+ [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
757
+ });
758
+ }
759
+ });
760
+ return data;
761
+ }
762
+ /**
763
+ * Retrieves usage statistics for the specified sub-account during the current billing period.
764
+ * @param handle - Handle of the sub-account to query usage stats for.
765
+ * @example
766
+ * ```ts
767
+ * const mailchannels = new MailChannels('your-api-key')
768
+ * const { usage } = await mailchannels.subAccounts.getUsage('validhandle123')
769
+ * ```
770
+ */
771
+ async getUsage(handle) {
772
+ const data = { usage: null, error: null };
773
+ if (!handle) {
774
+ data.error = "No handle provided.";
775
+ return data;
776
+ }
777
+ const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/usage`, {
648
778
  onResponseError: async ({ response: response2 }) => {
649
779
  data.error = getStatusError(response2, {
650
- [ErrorCode.NotFound]: "We could not find a customer that matched the customerHandle."
780
+ [ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
651
781
  });
652
782
  }
653
- }).catch(() => []);
654
- data.subscriptions = response;
783
+ }).catch(() => null);
784
+ if (!response) return data;
785
+ data.usage = {
786
+ endDate: response.period_end_date,
787
+ startDate: response.period_start_date,
788
+ total: response.total_usage
789
+ };
655
790
  return data;
656
791
  }
792
+ }
793
+
794
+ const mapBuckets = (arr) => {
795
+ return arr.map(({ count, period_start }) => ({ count, periodStart: period_start }));
796
+ };
797
+ class Metrics {
798
+ constructor(mailchannels) {
799
+ this.mailchannels = mailchannels;
800
+ }
657
801
  /**
658
- * Submit a false negative or false positive report.
659
- * @param options - The report options
802
+ * 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.
803
+ * @param options - Options to filter and customize the engagement metrics retrieval.
804
+ * @example
805
+ * ```ts
806
+ * const mailchannels = new MailChannels('your-api-key')
807
+ * const { engagement } = await mailchannels.metrics.engagement()
808
+ * ```
660
809
  */
661
- async report(options) {
662
- const data = { success: false, error: null };
663
- const { type, ...payload } = options;
664
- await this.mailchannels.post("/inbound/v1/report", {
810
+ async engagement(options) {
811
+ const data = { engagement: null, error: null };
812
+ const response = await this.mailchannels.get("/tx/v1/metrics/engagement", {
665
813
  query: {
666
- report_type: type
814
+ start_time: options?.startTime,
815
+ end_time: options?.endTime,
816
+ campaign_id: options?.campaignId,
817
+ interval: options?.interval
818
+ },
819
+ onResponseError: async ({ response: response2 }) => {
820
+ data.error = getStatusError(response2, {
821
+ [ErrorCode.BadRequest]: "Bad Request."
822
+ });
823
+ }
824
+ }).catch(() => null);
825
+ if (!response) return data;
826
+ data.engagement = {
827
+ buckets: {
828
+ click: mapBuckets(response.buckets.click),
829
+ clickTrackingDelivered: mapBuckets(response.buckets.click_tracking_delivered),
830
+ open: mapBuckets(response.buckets.open),
831
+ openTrackingDelivered: mapBuckets(response.buckets.open_tracking_delivered)
832
+ },
833
+ click: response.click,
834
+ clickTrackingDelivered: response.click_tracking_delivered,
835
+ endTime: response.end_time,
836
+ open: response.open,
837
+ openTrackingDelivered: response.open_tracking_delivered,
838
+ startTime: response.start_time
839
+ };
840
+ return data;
841
+ }
842
+ /**
843
+ * 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.
844
+ * @param options - Options to filter and customize the performance metrics retrieval.
845
+ * @example
846
+ * ```ts
847
+ * const mailchannels = new MailChannels('your-api-key')
848
+ * const { performance } = await mailchannels.metrics.performance()
849
+ * ```
850
+ */
851
+ async performance(options) {
852
+ const data = { performance: null, error: null };
853
+ const response = await this.mailchannels.get("/tx/v1/metrics/performance", {
854
+ query: {
855
+ start_time: options?.startTime,
856
+ end_time: options?.endTime,
857
+ campaign_id: options?.campaignId,
858
+ interval: options?.interval
859
+ },
860
+ onResponseError: async ({ response: response2 }) => {
861
+ data.error = getStatusError(response2, {
862
+ [ErrorCode.BadRequest]: "Bad Request."
863
+ });
864
+ }
865
+ }).catch(() => null);
866
+ if (!response) return data;
867
+ data.performance = {
868
+ bounced: response.bounced,
869
+ buckets: {
870
+ bounced: mapBuckets(response.buckets.bounced),
871
+ delivered: mapBuckets(response.buckets.delivered),
872
+ processed: mapBuckets(response.buckets.processed)
873
+ },
874
+ delivered: response.delivered,
875
+ endTime: response.end_time,
876
+ processed: response.processed,
877
+ startTime: response.start_time
878
+ };
879
+ return data;
880
+ }
881
+ /**
882
+ * Retrieve recipient behaviour metrics for messages sent from your account, including counts of unsubscribed events. Supports optional filters for time range, and campaign ID.
883
+ * @param options - Options to filter and customize the recipient behaviour metrics retrieval.
884
+ * @example
885
+ * ```ts
886
+ * const mailchannels = new MailChannels('your-api-key')
887
+ * const { behaviour } = await mailchannels.metrics.recipientBehaviour()
888
+ * ```
889
+ */
890
+ async recipientBehaviour(options) {
891
+ const data = { behaviour: null, error: null };
892
+ const response = await this.mailchannels.get("/tx/v1/metrics/recipient-behaviour", {
893
+ query: {
894
+ start_time: options?.startTime,
895
+ end_time: options?.endTime,
896
+ campaign_id: options?.campaignId,
897
+ interval: options?.interval
898
+ },
899
+ onResponseError: async ({ response: response2 }) => {
900
+ data.error = getStatusError(response2, {
901
+ [ErrorCode.BadRequest]: "Bad Request."
902
+ });
903
+ }
904
+ }).catch(() => null);
905
+ if (!response) return data;
906
+ data.behaviour = {
907
+ buckets: {
908
+ unsubscribeDelivered: mapBuckets(response.buckets.unsubscribe_delivered),
909
+ unsubscribed: mapBuckets(response.buckets.unsubscribed)
910
+ },
911
+ endTime: response.end_time,
912
+ startTime: response.start_time,
913
+ unsubscribeDelivered: response.unsubscribe_delivered,
914
+ unsubscribed: response.unsubscribed
915
+ };
916
+ return data;
917
+ }
918
+ /**
919
+ * 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.
920
+ * @param options - Options to filter and customize the volume metrics retrieval.
921
+ * @example
922
+ * ```ts
923
+ * const mailchannels = new MailChannels('your-api-key')
924
+ * const { volume } = await mailchannels.metrics.volume()
925
+ * ```
926
+ */
927
+ async volume(options) {
928
+ const data = { volume: null, error: null };
929
+ const response = await this.mailchannels.get("/tx/v1/metrics/volume", {
930
+ query: {
931
+ start_time: options?.startTime,
932
+ end_time: options?.endTime,
933
+ campaign_id: options?.campaignId,
934
+ interval: options?.interval
667
935
  },
936
+ onResponseError: async ({ response: response2 }) => {
937
+ data.error = getStatusError(response2, {
938
+ [ErrorCode.BadRequest]: "Bad Request."
939
+ });
940
+ }
941
+ }).catch(() => null);
942
+ if (!response) return data;
943
+ data.volume = {
944
+ buckets: {
945
+ delivered: mapBuckets(response.buckets.delivered),
946
+ dropped: mapBuckets(response.buckets.dropped),
947
+ processed: mapBuckets(response.buckets.processed)
948
+ },
949
+ delivered: response.delivered,
950
+ dropped: response.dropped,
951
+ endTime: response.end_time,
952
+ processed: response.processed,
953
+ startTime: response.start_time
954
+ };
955
+ return data;
956
+ }
957
+ /**
958
+ * Retrieves usage statistics during the current billing period.
959
+ * @example
960
+ * ```ts
961
+ * const mailchannels = new MailChannels('your-api-key')
962
+ * const { usage } = await mailchannels.metrics.usage()
963
+ * ```
964
+ */
965
+ async usage() {
966
+ const data = { usage: null, error: null };
967
+ const response = await this.mailchannels.get("/tx/v1/usage", {
968
+ onResponseError: async ({ response: response2 }) => {
969
+ data.error = getStatusError(response2);
970
+ }
971
+ }).catch(() => null);
972
+ if (!response) return data;
973
+ data.usage = {
974
+ endDate: response.period_end_date,
975
+ startDate: response.period_start_date,
976
+ total: response.total_usage
977
+ };
978
+ return data;
979
+ }
980
+ }
981
+
982
+ class Suppressions {
983
+ constructor(mailchannels) {
984
+ this.mailchannels = mailchannels;
985
+ }
986
+ /**
987
+ * 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.
988
+ * @param options - The details of the suppression entries to create.
989
+ * @example
990
+ * ```ts
991
+ * const mailchannels = new MailChannels('your-api-key')
992
+ * const response = await mailchannels.suppressions.create({
993
+ * // ...
994
+ * });
995
+ */
996
+ async create(options) {
997
+ const data = { success: false, error: null };
998
+ const { addToSubAccounts, entries } = options;
999
+ const payload = {
1000
+ add_to_sub_accounts: addToSubAccounts,
1001
+ suppression_entries: entries.map((entry) => ({
1002
+ notes: entry.notes,
1003
+ recipient: entry.recipient,
1004
+ suppression_types: Array.from(new Set(entry.types))
1005
+ }))
1006
+ };
1007
+ await this.mailchannels.post("/tx/v1/suppression-list", {
668
1008
  body: payload,
1009
+ ignoreResponseError: true,
669
1010
  onResponse: async ({ response }) => {
670
1011
  if (response.ok) {
671
1012
  data.success = true;
672
1013
  return;
673
1014
  }
674
- data.error = getStatusError(response);
1015
+ data.error = getStatusError(response, {
1016
+ [ErrorCode.BadRequest]: "Bad Request.",
1017
+ [ErrorCode.Conflict]: "Conflict. One or more suppression entries in the request already exist and cannot be created again.",
1018
+ [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."
1019
+ });
675
1020
  }
676
1021
  });
677
1022
  return data;
678
1023
  }
1024
+ /**
1025
+ * Deletes suppression entry associated with the account based on the specified recipient and source.
1026
+ * @param recipient - The email address of the suppression entry to delete.
1027
+ * @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.
1028
+ * @example
1029
+ * ```ts
1030
+ * const mailchannels = new MailChannels('your-api-key')
1031
+ * const response = await mailchannels.suppressions.delete('name@example.com', 'api');
1032
+ * ```
1033
+ */
1034
+ async delete(recipient, source) {
1035
+ const data = { success: false, error: null };
1036
+ await this.mailchannels.delete(`/tx/v1/suppression-list/recipients/${recipient}`, {
1037
+ query: {
1038
+ source
1039
+ },
1040
+ ignoreResponseError: true,
1041
+ onResponse: async ({ response }) => {
1042
+ if (response.ok) {
1043
+ data.success = true;
1044
+ return;
1045
+ }
1046
+ data.error = getStatusError(response, {
1047
+ [ErrorCode.BadRequest]: "Bad Request."
1048
+ });
1049
+ }
1050
+ });
1051
+ return data;
1052
+ }
1053
+ /**
1054
+ * 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`.
1055
+ * @example
1056
+ * ```ts
1057
+ * const mailchannels = new MailChannels('your-api-key')
1058
+ * const response = await mailchannels.suppressions.list();
1059
+ * ```
1060
+ * @param options - Options to filter and customize the suppression entries retrieval.
1061
+ */
1062
+ async list(options) {
1063
+ const data = { list: [], error: null };
1064
+ if (typeof options?.limit === "number" && (options.limit < 1 || options.limit > 1e3)) {
1065
+ data.error = "The limit must be between 1 and 1000.";
1066
+ return data;
1067
+ }
1068
+ if (typeof options?.offset === "number" && options.offset < 0) {
1069
+ data.error = "Offset must be greater than or equal to 0.";
1070
+ return data;
1071
+ }
1072
+ const payload = {
1073
+ recipient: options?.recipient,
1074
+ source: options?.source,
1075
+ created_before: options?.createdBefore,
1076
+ created_after: options?.createdAfter,
1077
+ limit: options?.limit,
1078
+ offset: options?.offset
1079
+ };
1080
+ const response = await this.mailchannels.get("/tx/v1/suppression-list", {
1081
+ query: payload,
1082
+ onResponseError: async ({ response: response2 }) => {
1083
+ data.error = getStatusError(response2, {
1084
+ [ErrorCode.BadRequest]: "Bad Request."
1085
+ });
1086
+ }
1087
+ }).catch(() => null);
1088
+ if (!response) return data;
1089
+ data.list = response.suppression_list.map((entry) => ({
1090
+ createdAt: entry.created_at,
1091
+ notes: entry.notes,
1092
+ recipient: entry.recipient,
1093
+ sender: entry.sender,
1094
+ source: entry.source,
1095
+ types: entry.suppression_types
1096
+ }));
1097
+ return data;
1098
+ }
679
1099
  }
680
1100
 
681
1101
  class Domains {
@@ -1086,6 +1506,105 @@ class Domains {
1086
1506
  }
1087
1507
  }
1088
1508
 
1509
+ class Lists {
1510
+ constructor(mailchannels) {
1511
+ this.mailchannels = mailchannels;
1512
+ }
1513
+ /**
1514
+ * Add item to account-level list
1515
+ * @param options - The options for the list entry to add.
1516
+ * @example
1517
+ * ```ts
1518
+ * const mailchannels = new MailChannels('your-api-key')
1519
+ * const { entry } = await mailchannels.lists.addListEntry({
1520
+ * listName: 'safelist',
1521
+ * item: 'name@domain.com'
1522
+ * })
1523
+ * ```
1524
+ */
1525
+ async addListEntry(options) {
1526
+ const { listName, item } = options;
1527
+ const data = { entry: null, error: null };
1528
+ if (!listName) {
1529
+ data.error = "No list name provided.";
1530
+ return data;
1531
+ }
1532
+ const response = await this.mailchannels.post(`/inbound/v1/lists/${listName}`, {
1533
+ body: { item },
1534
+ onResponseError: async ({ response: response2 }) => {
1535
+ data.error = getStatusError(response2);
1536
+ }
1537
+ }).catch(() => null);
1538
+ if (!response) return data;
1539
+ data.entry = {
1540
+ action: response.action,
1541
+ item: response.item,
1542
+ type: response.item_type
1543
+ };
1544
+ return data;
1545
+ }
1546
+ /**
1547
+ * Get account-level list entries.
1548
+ * @param listName - The name of the list to fetch. This can be a `blocklist`, `safelist`, `blacklist`, or `whitelist`.
1549
+ * @example
1550
+ * ```ts
1551
+ * const mailchannels = new MailChannels('your-api-key')
1552
+ * const { entries } = await mailchannels.lists.listEntries('safelist')
1553
+ * ```
1554
+ */
1555
+ async listEntries(listName) {
1556
+ const data = { entries: [], error: null };
1557
+ if (!listName) {
1558
+ data.error = "No list name provided.";
1559
+ return data;
1560
+ }
1561
+ const response = await this.mailchannels.get(`/inbound/v1/lists/${listName}`, {
1562
+ onResponseError: async ({ response: response2 }) => {
1563
+ data.error = getStatusError(response2);
1564
+ }
1565
+ }).catch(() => null);
1566
+ if (!response) return data;
1567
+ data.entries = response.map(({ action, item, item_type }) => ({
1568
+ action,
1569
+ item,
1570
+ type: item_type
1571
+ }));
1572
+ return data;
1573
+ }
1574
+ /**
1575
+ * Delete item from account-level list.
1576
+ * @param options - The options for the list entry to delete.
1577
+ * @example
1578
+ * ```ts
1579
+ * const mailchannels = new MailChannels('your-api-key')
1580
+ * const { success } = await mailchannels.lists.deleteListEntry({
1581
+ * listName: 'safelist',
1582
+ * item: 'name@domain.com'
1583
+ * })
1584
+ * ```
1585
+ */
1586
+ async deleteListEntry(options) {
1587
+ const { listName, item } = options;
1588
+ const data = { success: false, error: null };
1589
+ if (!listName) {
1590
+ data.error = "No list name provided.";
1591
+ return data;
1592
+ }
1593
+ await this.mailchannels.delete(`/inbound/v1/lists/${listName}`, {
1594
+ query: { item },
1595
+ ignoreResponseError: true,
1596
+ onResponse: async ({ response }) => {
1597
+ if (response.ok) {
1598
+ data.success = true;
1599
+ return;
1600
+ }
1601
+ data.error = getStatusError(response);
1602
+ }
1603
+ });
1604
+ return data;
1605
+ }
1606
+ }
1607
+
1089
1608
  class Users {
1090
1609
  constructor(mailchannels) {
1091
1610
  this.mailchannels = mailchannels;
@@ -1256,93 +1775,71 @@ class Users {
1256
1775
  }
1257
1776
  }
1258
1777
 
1259
- class Lists {
1778
+ class Service {
1260
1779
  constructor(mailchannels) {
1261
1780
  this.mailchannels = mailchannels;
1262
1781
  }
1263
1782
  /**
1264
- * Add item to account-level list
1265
- * @param options - The options for the list entry to add.
1783
+ * Retrieve the condition of the service
1266
1784
  * @example
1267
1785
  * ```ts
1268
1786
  * const mailchannels = new MailChannels('your-api-key')
1269
- * const { entry } = await mailchannels.lists.addListEntry({
1270
- * listName: 'safelist',
1271
- * item: 'name@domain.com'
1272
- * })
1787
+ * const { success } = await mailchannels.service.status()
1273
1788
  * ```
1274
1789
  */
1275
- async addListEntry(options) {
1276
- const { listName, item } = options;
1277
- const data = { entry: null, error: null };
1278
- if (!listName) {
1279
- data.error = "No list name provided.";
1280
- return data;
1281
- }
1282
- const response = await this.mailchannels.post(`/inbound/v1/lists/${listName}`, {
1283
- body: { item },
1284
- onResponseError: async ({ response: response2 }) => {
1285
- data.error = getStatusError(response2);
1790
+ async status() {
1791
+ const data = { success: false, error: null };
1792
+ await this.mailchannels.get("/inbound/v1/status", {
1793
+ ignoreResponseError: true,
1794
+ onResponse: async ({ response }) => {
1795
+ if (response.ok) {
1796
+ data.success = true;
1797
+ return;
1798
+ }
1799
+ data.error = getStatusError(response);
1286
1800
  }
1287
- }).catch(() => null);
1288
- if (!response) return data;
1289
- data.entry = {
1290
- action: response.action,
1291
- item: response.item,
1292
- type: response.item_type
1293
- };
1801
+ });
1294
1802
  return data;
1295
1803
  }
1296
1804
  /**
1297
- * Get account-level list entries.
1298
- * @param listName - The name of the list to fetch. This can be a `blocklist`, `safelist`, `blacklist`, or `whitelist`.
1805
+ * Get a list of your subscriptions to MailChannels Inbound
1299
1806
  * @example
1300
1807
  * ```ts
1301
1808
  * const mailchannels = new MailChannels('your-api-key')
1302
- * const { entries } = await mailchannels.lists.listEntries('safelist')
1809
+ * const { subscriptions } = await mailchannels.service.subscriptions()
1303
1810
  * ```
1304
1811
  */
1305
- async listEntries(listName) {
1306
- const data = { entries: [], error: null };
1307
- if (!listName) {
1308
- data.error = "No list name provided.";
1309
- return data;
1310
- }
1311
- const response = await this.mailchannels.get(`/inbound/v1/lists/${listName}`, {
1812
+ async subscriptions() {
1813
+ const data = { subscriptions: [], error: null };
1814
+ const response = await this.mailchannels.get("/inbound/v1/subscriptions", {
1312
1815
  onResponseError: async ({ response: response2 }) => {
1313
- data.error = getStatusError(response2);
1816
+ data.error = getStatusError(response2, {
1817
+ [ErrorCode.NotFound]: "We could not find a customer that matched the customerHandle."
1818
+ });
1314
1819
  }
1315
- }).catch(() => null);
1316
- if (!response) return data;
1317
- data.entries = response.map(({ action, item, item_type }) => ({
1318
- action,
1319
- item,
1320
- type: item_type
1321
- }));
1820
+ }).catch(() => []);
1821
+ data.subscriptions = response;
1322
1822
  return data;
1323
1823
  }
1324
1824
  /**
1325
- * Delete item from account-level list.
1326
- * @param options - The options for the list entry to delete.
1825
+ * Submit a false negative or false positive report.
1826
+ * @param options - The report options
1327
1827
  * @example
1328
1828
  * ```ts
1329
1829
  * const mailchannels = new MailChannels('your-api-key')
1330
- * const { success } = await mailchannels.lists.deleteListEntry({
1331
- * listName: 'safelist',
1332
- * item: 'name@domain.com'
1830
+ * const { success, error } = await mailchannels.service.report({
1831
+ * // ...
1333
1832
  * })
1334
1833
  * ```
1335
1834
  */
1336
- async deleteListEntry(options) {
1337
- const { listName, item } = options;
1835
+ async report(options) {
1338
1836
  const data = { success: false, error: null };
1339
- if (!listName) {
1340
- data.error = "No list name provided.";
1341
- return data;
1342
- }
1343
- await this.mailchannels.delete(`/inbound/v1/lists/${listName}`, {
1344
- query: { item },
1345
- ignoreResponseError: true,
1837
+ const { type, ...payload } = options;
1838
+ await this.mailchannels.post("/inbound/v1/report", {
1839
+ query: {
1840
+ report_type: type
1841
+ },
1842
+ body: payload,
1346
1843
  onResponse: async ({ response }) => {
1347
1844
  if (response.ok) {
1348
1845
  data.success = true;
@@ -1355,4 +1852,4 @@ class Lists {
1355
1852
  }
1356
1853
  }
1357
1854
 
1358
- export { Domains, Emails, Lists, Service, SubAccounts, Users, Webhooks };
1855
+ export { Domains, Emails, Lists, Metrics, Service, SubAccounts, Suppressions, Users, Webhooks };