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