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