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.
- package/README.md +10 -8
- package/dist/mailchannels.d.mts +311 -286
- package/dist/mailchannels.d.ts +311 -286
- package/dist/mailchannels.mjs +696 -483
- package/package.json +12 -12
package/dist/mailchannels.mjs
CHANGED
|
@@ -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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
144
|
+
const result = { success: false, data: null, error: null };
|
|
118
145
|
const parsedFrom = parseRecipient(from);
|
|
119
146
|
if (!parsedFrom || !parsedFrom.email) {
|
|
120
|
-
|
|
121
|
-
return
|
|
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
|
-
|
|
126
|
-
return
|
|
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
|
-
|
|
130
|
-
return
|
|
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
|
-
|
|
191
|
+
result.success = true;
|
|
165
192
|
return;
|
|
166
193
|
}
|
|
167
|
-
|
|
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
|
|
175
|
-
|
|
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((
|
|
179
|
-
index:
|
|
180
|
-
messageId:
|
|
181
|
-
reason:
|
|
182
|
-
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
|
|
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
|
|
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
|
-
|
|
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
|
|
227
|
-
data
|
|
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
|
|
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 {
|
|
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
|
|
282
|
+
const result = { data: null, error: null };
|
|
256
283
|
if (!options.selector || options.selector.length > 63) {
|
|
257
|
-
|
|
284
|
+
result.error = "Selector must be between 1 and 63 characters.";
|
|
258
285
|
}
|
|
259
|
-
if (
|
|
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
|
-
|
|
295
|
+
result.error = getStatusError(response2, {
|
|
269
296
|
[ErrorCode.BadRequest]: "Bad Request.",
|
|
270
|
-
[ErrorCode.Conflict]: "Key pair already created for
|
|
297
|
+
[ErrorCode.Conflict]: "Key pair already created for domain, and selector."
|
|
271
298
|
});
|
|
272
299
|
}
|
|
273
300
|
}).catch(() => null);
|
|
274
|
-
if (!response) return
|
|
275
|
-
data
|
|
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
|
|
314
|
+
});
|
|
315
|
+
return result;
|
|
287
316
|
}
|
|
288
317
|
/**
|
|
289
|
-
* Search for DKIM keys by
|
|
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 {
|
|
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
|
|
330
|
+
const result = { data: null, error: null };
|
|
302
331
|
if (options?.selector && options.selector.length > 63) {
|
|
303
|
-
|
|
304
|
-
return
|
|
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
|
-
|
|
308
|
-
return
|
|
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
|
-
|
|
312
|
-
return
|
|
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
|
-
|
|
353
|
+
result.error = getStatusError(response2, {
|
|
325
354
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
326
355
|
});
|
|
327
356
|
}
|
|
328
357
|
}).catch(() => null);
|
|
329
|
-
if (!response) return
|
|
330
|
-
data
|
|
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
|
|
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
|
|
387
|
+
const result = { success: false, error: null };
|
|
357
388
|
if (!options.selector || options.selector.length > 63) {
|
|
358
|
-
|
|
359
|
-
return
|
|
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
|
-
|
|
399
|
+
result.success = true;
|
|
370
400
|
return;
|
|
371
401
|
}
|
|
372
|
-
|
|
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
|
|
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
|
|
505
|
+
const result = { success: false, error: null };
|
|
397
506
|
if (!endpoint) {
|
|
398
|
-
|
|
399
|
-
return
|
|
507
|
+
result.error = "No endpoint provided.";
|
|
508
|
+
return result;
|
|
400
509
|
}
|
|
401
510
|
if (endpoint.length > 8e3) {
|
|
402
|
-
|
|
403
|
-
return
|
|
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
|
-
|
|
521
|
+
result.success = true;
|
|
413
522
|
return;
|
|
414
523
|
}
|
|
415
|
-
|
|
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
|
|
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 {
|
|
536
|
+
* const { data, error } = await mailchannels.webhooks.list()
|
|
428
537
|
* ```
|
|
429
538
|
*/
|
|
430
539
|
async list() {
|
|
431
|
-
const
|
|
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
|
-
|
|
543
|
+
result.error = getStatusError(response2);
|
|
435
544
|
}
|
|
436
|
-
}).catch(() =>
|
|
437
|
-
|
|
438
|
-
|
|
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
|
|
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
|
-
|
|
569
|
+
result.error = getStatusError(response);
|
|
455
570
|
return;
|
|
456
571
|
}
|
|
457
|
-
|
|
572
|
+
result.success = true;
|
|
458
573
|
}
|
|
459
574
|
});
|
|
460
|
-
return
|
|
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 {
|
|
583
|
+
* const { data, error } = await mailchannels.webhooks.getSigningKey('key-id')
|
|
469
584
|
* ```
|
|
470
585
|
*/
|
|
471
586
|
async getSigningKey(id) {
|
|
472
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
485
|
-
|
|
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 {
|
|
609
|
+
* const { data, error } = await mailchannels.webhooks.validate('optional-request-id')
|
|
494
610
|
* ```
|
|
495
611
|
*/
|
|
496
612
|
async validate(requestId) {
|
|
497
|
-
const
|
|
613
|
+
const result = { data: null, error: null };
|
|
498
614
|
if (requestId && requestId.length > 28) {
|
|
499
|
-
|
|
500
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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 {
|
|
651
|
+
* const { data, error } = await mailchannels.subAccounts.create('My Company', 'validhandle123')
|
|
535
652
|
* ```
|
|
536
653
|
*/
|
|
537
654
|
async create(companyName, handle) {
|
|
538
|
-
const
|
|
655
|
+
const result = { data: null, error: null };
|
|
539
656
|
const isValidCompany = SubAccounts.COMPANY_PATTERN.test(companyName);
|
|
540
657
|
if (!isValidCompany) {
|
|
541
|
-
|
|
542
|
-
return
|
|
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
|
-
|
|
548
|
-
return
|
|
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
|
-
|
|
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
|
|
564
|
-
data
|
|
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
|
|
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 {
|
|
694
|
+
* const { data, error } = await mailchannels.subAccounts.list()
|
|
578
695
|
* ```
|
|
579
696
|
*/
|
|
580
697
|
async list(options) {
|
|
581
|
-
const
|
|
698
|
+
const result = { data: null, error: null };
|
|
582
699
|
if (typeof options?.limit === "number" && (options.limit < 1 || options.limit > 1e3)) {
|
|
583
|
-
|
|
584
|
-
return
|
|
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
|
-
|
|
588
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
596
|
-
|
|
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
|
|
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
|
|
735
|
+
const result = { success: false, error: null };
|
|
613
736
|
if (!handle) {
|
|
614
|
-
|
|
615
|
-
return
|
|
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
|
-
|
|
744
|
+
result.error = getStatusError(response);
|
|
622
745
|
return;
|
|
623
746
|
}
|
|
624
|
-
|
|
747
|
+
result.success = true;
|
|
625
748
|
}
|
|
626
749
|
});
|
|
627
|
-
return
|
|
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
|
|
762
|
+
const result = { success: false, error: null };
|
|
640
763
|
if (!handle) {
|
|
641
|
-
|
|
642
|
-
return
|
|
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
|
-
|
|
771
|
+
result.success = true;
|
|
649
772
|
return;
|
|
650
773
|
}
|
|
651
|
-
|
|
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
|
|
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
|
|
791
|
+
const result = { success: false, error: null };
|
|
669
792
|
if (!handle) {
|
|
670
|
-
|
|
671
|
-
return
|
|
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
|
-
|
|
800
|
+
result.success = true;
|
|
678
801
|
return;
|
|
679
802
|
}
|
|
680
|
-
|
|
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
|
|
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 {
|
|
817
|
+
* const { data, error } = await mailchannels.subAccounts.createApiKey('validhandle123')
|
|
695
818
|
* ```
|
|
696
819
|
*/
|
|
697
820
|
async createApiKey(handle) {
|
|
698
|
-
const
|
|
821
|
+
const result = { data: null, error: null };
|
|
699
822
|
if (!handle) {
|
|
700
|
-
|
|
701
|
-
return
|
|
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
|
-
|
|
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
|
|
713
|
-
data
|
|
835
|
+
if (!response) return result;
|
|
836
|
+
result.data = clean({
|
|
714
837
|
id: response.id,
|
|
715
838
|
value: response.key
|
|
716
|
-
};
|
|
717
|
-
return
|
|
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 {
|
|
849
|
+
* const { data, error } = await mailchannels.subAccounts.listApiKeys('validhandle123')
|
|
727
850
|
* ```
|
|
728
851
|
*/
|
|
729
852
|
async listApiKeys(handle, options) {
|
|
730
|
-
const
|
|
853
|
+
const result = { data: null, error: null };
|
|
731
854
|
if (!handle) {
|
|
732
|
-
|
|
733
|
-
return
|
|
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
|
-
|
|
737
|
-
return
|
|
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
|
-
|
|
741
|
-
return
|
|
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
|
-
|
|
868
|
+
result.error = getStatusError(response2, {
|
|
746
869
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
747
870
|
});
|
|
748
871
|
}
|
|
749
|
-
}).catch(() =>
|
|
750
|
-
|
|
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
|
|
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
|
|
891
|
+
const result = { success: false, error: null };
|
|
768
892
|
if (!handle) {
|
|
769
|
-
|
|
770
|
-
return
|
|
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
|
-
|
|
900
|
+
result.success = true;
|
|
777
901
|
return;
|
|
778
902
|
}
|
|
779
|
-
|
|
903
|
+
result.error = getStatusError(response, {
|
|
780
904
|
[ErrorCode.BadRequest]: "Missing or invalid API key ID."
|
|
781
905
|
});
|
|
782
906
|
}
|
|
783
907
|
});
|
|
784
|
-
return
|
|
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 {
|
|
916
|
+
* const { data, error } = await mailchannels.subAccounts.createSmtpPassword('validhandle123')
|
|
793
917
|
* ```
|
|
794
918
|
*/
|
|
795
919
|
async createSmtpPassword(handle) {
|
|
796
|
-
const
|
|
920
|
+
const result = { data: null, error: null };
|
|
797
921
|
if (!handle) {
|
|
798
|
-
|
|
799
|
-
return
|
|
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
|
-
|
|
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
|
|
811
|
-
data
|
|
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
|
|
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 {
|
|
948
|
+
* const { data, error } = await mailchannels.subAccounts.listSmtpPasswords('validhandle123')
|
|
825
949
|
* ```
|
|
826
950
|
*/
|
|
827
951
|
async listSmtpPasswords(handle) {
|
|
828
|
-
const
|
|
952
|
+
const result = { data: null, error: null };
|
|
829
953
|
if (!handle) {
|
|
830
|
-
|
|
831
|
-
return
|
|
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
|
-
|
|
959
|
+
result.error = getStatusError(response2, {
|
|
836
960
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
837
961
|
});
|
|
838
962
|
}
|
|
839
|
-
}).catch(() =>
|
|
840
|
-
|
|
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
|
|
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
|
|
983
|
+
const result = { success: false, error: null };
|
|
859
984
|
if (!handle) {
|
|
860
|
-
|
|
861
|
-
return
|
|
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
|
-
|
|
992
|
+
result.success = true;
|
|
868
993
|
return;
|
|
869
994
|
}
|
|
870
|
-
|
|
995
|
+
result.error = getStatusError(response, {
|
|
871
996
|
[ErrorCode.BadRequest]: "Missing or invalid SMTP password ID."
|
|
872
997
|
});
|
|
873
998
|
}
|
|
874
999
|
});
|
|
875
|
-
return
|
|
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 {
|
|
1008
|
+
* const { data, error } = await mailchannels.subAccounts.getLimit('validhandle123')
|
|
884
1009
|
* ```
|
|
885
1010
|
*/
|
|
886
1011
|
async getLimit(handle) {
|
|
887
|
-
const
|
|
1012
|
+
const result = { data: null, error: null };
|
|
888
1013
|
if (!handle) {
|
|
889
|
-
|
|
890
|
-
return
|
|
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
|
-
|
|
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
|
|
900
|
-
data
|
|
901
|
-
return
|
|
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
|
|
1039
|
+
const result = { success: false, error: null };
|
|
915
1040
|
if (!handle) {
|
|
916
|
-
|
|
917
|
-
return
|
|
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
|
-
|
|
1049
|
+
result.success = true;
|
|
925
1050
|
return;
|
|
926
1051
|
}
|
|
927
|
-
|
|
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
|
|
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
|
|
1070
|
+
const result = { success: false, error: null };
|
|
946
1071
|
if (!handle) {
|
|
947
|
-
|
|
948
|
-
return
|
|
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
|
-
|
|
1079
|
+
result.success = true;
|
|
955
1080
|
return;
|
|
956
1081
|
}
|
|
957
|
-
|
|
1082
|
+
result.error = getStatusError(response, {
|
|
958
1083
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
959
1084
|
});
|
|
960
1085
|
}
|
|
961
1086
|
});
|
|
962
|
-
return
|
|
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 {
|
|
1095
|
+
* const { data, error } = await mailchannels.subAccounts.getUsage('validhandle123')
|
|
971
1096
|
* ```
|
|
972
1097
|
*/
|
|
973
1098
|
async getUsage(handle) {
|
|
974
|
-
const
|
|
1099
|
+
const result = { data: null, error: null };
|
|
975
1100
|
if (!handle) {
|
|
976
|
-
|
|
977
|
-
return
|
|
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
|
-
|
|
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
|
|
987
|
-
data
|
|
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
|
|
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 {
|
|
1134
|
+
* const { data, error } = await mailchannels.metrics.engagement()
|
|
1010
1135
|
* ```
|
|
1011
1136
|
*/
|
|
1012
1137
|
async engagement(options) {
|
|
1013
|
-
const
|
|
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
|
-
|
|
1147
|
+
result.error = getStatusError(response2, {
|
|
1023
1148
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1024
1149
|
});
|
|
1025
1150
|
}
|
|
1026
|
-
}).catch(() =>
|
|
1027
|
-
|
|
1028
|
-
|
|
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
|
|
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 {
|
|
1180
|
+
* const { data, error } = await mailchannels.metrics.performance()
|
|
1051
1181
|
* ```
|
|
1052
1182
|
*/
|
|
1053
1183
|
async performance(options) {
|
|
1054
|
-
const
|
|
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
|
-
|
|
1193
|
+
result.error = getStatusError(response2, {
|
|
1064
1194
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1065
1195
|
});
|
|
1066
1196
|
}
|
|
1067
|
-
}).catch(() =>
|
|
1068
|
-
|
|
1069
|
-
|
|
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
|
|
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 {
|
|
1224
|
+
* const { data, error } = await mailchannels.metrics.recipientBehaviour()
|
|
1090
1225
|
* ```
|
|
1091
1226
|
*/
|
|
1092
1227
|
async recipientBehaviour(options) {
|
|
1093
|
-
const
|
|
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
|
-
|
|
1237
|
+
result.error = getStatusError(response2, {
|
|
1103
1238
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1104
1239
|
});
|
|
1105
1240
|
}
|
|
1106
|
-
}).catch(() =>
|
|
1107
|
-
|
|
1108
|
-
|
|
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
|
|
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 {
|
|
1266
|
+
* const { data, error } = await mailchannels.metrics.volume()
|
|
1127
1267
|
* ```
|
|
1128
1268
|
*/
|
|
1129
1269
|
async volume(options) {
|
|
1130
|
-
const
|
|
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
|
-
|
|
1279
|
+
result.error = getStatusError(response2, {
|
|
1140
1280
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1141
1281
|
});
|
|
1142
1282
|
}
|
|
1143
|
-
}).catch(() =>
|
|
1144
|
-
|
|
1145
|
-
|
|
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
|
|
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 {
|
|
1309
|
+
* const { data, error } = await mailchannels.metrics.usage()
|
|
1165
1310
|
* ```
|
|
1166
1311
|
*/
|
|
1167
1312
|
async usage() {
|
|
1168
|
-
const
|
|
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
|
-
|
|
1316
|
+
result.error = getStatusError(response2);
|
|
1172
1317
|
}
|
|
1173
|
-
}).catch(() =>
|
|
1174
|
-
|
|
1175
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1407
|
+
result.success = true;
|
|
1215
1408
|
return;
|
|
1216
1409
|
}
|
|
1217
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1438
|
+
result.success = true;
|
|
1246
1439
|
return;
|
|
1247
1440
|
}
|
|
1248
|
-
|
|
1441
|
+
result.error = getStatusError(response, {
|
|
1249
1442
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1250
1443
|
});
|
|
1251
1444
|
}
|
|
1252
1445
|
});
|
|
1253
|
-
return
|
|
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 {
|
|
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
|
|
1458
|
+
const result = { data: null, error: null };
|
|
1266
1459
|
if (typeof options?.limit === "number" && (options.limit < 1 || options.limit > 1e3)) {
|
|
1267
|
-
|
|
1268
|
-
return
|
|
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
|
-
|
|
1272
|
-
return
|
|
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
|
-
|
|
1478
|
+
result.error = getStatusError(response2, {
|
|
1286
1479
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1287
1480
|
});
|
|
1288
1481
|
}
|
|
1289
1482
|
}).catch(() => null);
|
|
1290
|
-
if (!response) return
|
|
1291
|
-
data
|
|
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
|
|
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
|
|
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
|
-
|
|
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(() =>
|
|
1336
|
-
|
|
1337
|
-
|
|
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 {
|
|
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
|
|
1559
|
+
const result = { data: null, error: null };
|
|
1362
1560
|
if (!domains || !domains.length) {
|
|
1363
|
-
|
|
1364
|
-
return
|
|
1561
|
+
result.error = "No domains provided.";
|
|
1562
|
+
return result;
|
|
1365
1563
|
}
|
|
1366
1564
|
if (domains.length > 1e3) {
|
|
1367
|
-
|
|
1368
|
-
return
|
|
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
|
-
|
|
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(() =>
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
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 {
|
|
1597
|
+
* const { data, error } = await mailchannels.domains.list()
|
|
1395
1598
|
* ```
|
|
1396
1599
|
*/
|
|
1397
1600
|
async list(options) {
|
|
1398
|
-
const
|
|
1601
|
+
const result = { data: null, error: null };
|
|
1399
1602
|
if (typeof options?.limit === "number" && (options.limit < 1 || options.limit > 5e3)) {
|
|
1400
|
-
|
|
1401
|
-
return
|
|
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
|
-
|
|
1405
|
-
return
|
|
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
|
-
|
|
1613
|
+
result.error = getStatusError(response2);
|
|
1411
1614
|
}
|
|
1412
1615
|
}).catch(() => null);
|
|
1413
|
-
if (!response) return
|
|
1414
|
-
data
|
|
1415
|
-
|
|
1416
|
-
|
|
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
|
|
1633
|
+
const result = { success: false, error: null };
|
|
1429
1634
|
if (!domain) {
|
|
1430
|
-
|
|
1431
|
-
return
|
|
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
|
-
|
|
1642
|
+
result.success = true;
|
|
1438
1643
|
return;
|
|
1439
1644
|
}
|
|
1440
|
-
|
|
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
|
|
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 {
|
|
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
|
|
1668
|
+
const result = { data: null, error: null };
|
|
1464
1669
|
if (!domain) {
|
|
1465
|
-
|
|
1466
|
-
return
|
|
1670
|
+
result.error = "No domain provided.";
|
|
1671
|
+
return result;
|
|
1467
1672
|
}
|
|
1468
1673
|
if (!listName) {
|
|
1469
|
-
|
|
1470
|
-
return
|
|
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
|
-
|
|
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
|
|
1482
|
-
data
|
|
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
|
|
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 {
|
|
1701
|
+
* const { data, error } = await mailchannels.domains.listEntries('example.com', 'safelist')
|
|
1497
1702
|
* ```
|
|
1498
1703
|
*/
|
|
1499
1704
|
async listEntries(domain, listName) {
|
|
1500
|
-
const
|
|
1705
|
+
const result = { data: null, error: null };
|
|
1501
1706
|
if (!domain) {
|
|
1502
|
-
|
|
1503
|
-
return
|
|
1707
|
+
result.error = "No domain provided.";
|
|
1708
|
+
return result;
|
|
1504
1709
|
}
|
|
1505
1710
|
if (!listName) {
|
|
1506
|
-
|
|
1507
|
-
return
|
|
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
|
-
|
|
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
|
|
1518
|
-
data
|
|
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
|
|
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
|
|
1745
|
+
const result = { success: false, error: null };
|
|
1541
1746
|
if (!domain) {
|
|
1542
|
-
|
|
1543
|
-
return
|
|
1747
|
+
result.error = "No domain provided.";
|
|
1748
|
+
return result;
|
|
1544
1749
|
}
|
|
1545
1750
|
if (!listName) {
|
|
1546
|
-
|
|
1547
|
-
return
|
|
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
|
-
|
|
1759
|
+
result.success = true;
|
|
1555
1760
|
return;
|
|
1556
1761
|
}
|
|
1557
|
-
|
|
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
|
|
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 {
|
|
1776
|
+
* const { data, error } = await mailchannels.domains.createLoginLink('example.com')
|
|
1572
1777
|
* ```
|
|
1573
1778
|
*/
|
|
1574
1779
|
async createLoginLink(domain) {
|
|
1575
|
-
const
|
|
1780
|
+
const result = { data: null, error: null };
|
|
1576
1781
|
if (!domain) {
|
|
1577
|
-
|
|
1578
|
-
return
|
|
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
|
-
|
|
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
|
|
1590
|
-
data
|
|
1591
|
-
|
|
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
|
|
1818
|
+
const result = { success: false, error: null };
|
|
1612
1819
|
if (!domain) {
|
|
1613
|
-
|
|
1614
|
-
return
|
|
1820
|
+
result.error = "No domain provided.";
|
|
1821
|
+
return result;
|
|
1615
1822
|
}
|
|
1616
1823
|
if (records.length > 10) {
|
|
1617
|
-
|
|
1618
|
-
return
|
|
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
|
-
|
|
1832
|
+
result.success = true;
|
|
1626
1833
|
return;
|
|
1627
1834
|
}
|
|
1628
|
-
|
|
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
|
|
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 {
|
|
1850
|
+
* const { data, error } = await mailchannels.domains.listDownstreamAddresses('example.com')
|
|
1644
1851
|
* ```
|
|
1645
1852
|
*/
|
|
1646
1853
|
async listDownstreamAddresses(domain, options) {
|
|
1647
|
-
const
|
|
1854
|
+
const result = { data: null, error: null };
|
|
1648
1855
|
if (!domain) {
|
|
1649
|
-
|
|
1650
|
-
return
|
|
1856
|
+
result.error = "No domain provided.";
|
|
1857
|
+
return result;
|
|
1651
1858
|
}
|
|
1652
1859
|
if (typeof options?.limit === "number" && options.limit < 1) {
|
|
1653
|
-
|
|
1654
|
-
return
|
|
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
|
-
|
|
1658
|
-
return
|
|
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
|
-
|
|
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
|
|
1670
|
-
data
|
|
1671
|
-
return
|
|
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
|
|
1891
|
+
const result = { success: false, error: null };
|
|
1685
1892
|
if (!domain) {
|
|
1686
|
-
|
|
1687
|
-
return
|
|
1893
|
+
result.error = "No domain provided.";
|
|
1894
|
+
return result;
|
|
1688
1895
|
}
|
|
1689
1896
|
if (!key) {
|
|
1690
|
-
|
|
1691
|
-
return
|
|
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
|
-
|
|
1905
|
+
result.success = true;
|
|
1699
1906
|
return;
|
|
1700
1907
|
}
|
|
1701
|
-
|
|
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
|
|
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 {
|
|
1922
|
+
* const { data, error } = await mailchannels.domains.bulkCreateLoginLinks(['example.com', 'example2.com'])
|
|
1716
1923
|
* ```
|
|
1717
1924
|
*/
|
|
1718
1925
|
async bulkCreateLoginLinks(domains) {
|
|
1719
|
-
const
|
|
1926
|
+
const result = { data: null, error: null };
|
|
1720
1927
|
if (!domains || !domains.length) {
|
|
1721
|
-
|
|
1722
|
-
return
|
|
1928
|
+
result.error = "No domains provided.";
|
|
1929
|
+
return result;
|
|
1723
1930
|
}
|
|
1724
1931
|
if (domains.length > 1e3) {
|
|
1725
|
-
|
|
1726
|
-
return
|
|
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
|
-
|
|
1940
|
+
result.error = getStatusError(response2, {
|
|
1734
1941
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1735
1942
|
});
|
|
1736
1943
|
}
|
|
1737
1944
|
}).catch(() => null);
|
|
1738
|
-
if (!response) return
|
|
1739
|
-
data
|
|
1740
|
-
return
|
|
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 {
|
|
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
|
|
1969
|
+
const result = { data: null, error: null };
|
|
1763
1970
|
if (!listName) {
|
|
1764
|
-
|
|
1765
|
-
return
|
|
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
|
-
|
|
1977
|
+
result.error = getStatusError(response2);
|
|
1771
1978
|
}
|
|
1772
1979
|
}).catch(() => null);
|
|
1773
|
-
if (!response) return
|
|
1774
|
-
data
|
|
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
|
|
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 {
|
|
1994
|
+
* const { data, error } = await mailchannels.lists.listEntries('safelist')
|
|
1788
1995
|
* ```
|
|
1789
1996
|
*/
|
|
1790
1997
|
async listEntries(listName) {
|
|
1791
|
-
const
|
|
1998
|
+
const result = { data: null, error: null };
|
|
1792
1999
|
if (!listName) {
|
|
1793
|
-
|
|
1794
|
-
return
|
|
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
|
-
|
|
2005
|
+
result.error = getStatusError(response2);
|
|
1799
2006
|
}
|
|
1800
2007
|
}).catch(() => null);
|
|
1801
|
-
if (!response) return
|
|
1802
|
-
data
|
|
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
|
|
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
|
|
2030
|
+
const result = { success: false, error: null };
|
|
1824
2031
|
if (!listName) {
|
|
1825
|
-
|
|
1826
|
-
return
|
|
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
|
-
|
|
2040
|
+
result.success = true;
|
|
1834
2041
|
return;
|
|
1835
2042
|
}
|
|
1836
|
-
|
|
2043
|
+
result.error = getStatusError(response);
|
|
1837
2044
|
}
|
|
1838
2045
|
});
|
|
1839
|
-
return
|
|
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 {
|
|
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
|
|
2068
|
+
const result = { data: null, error: null };
|
|
1862
2069
|
if (!email) {
|
|
1863
|
-
|
|
1864
|
-
return
|
|
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
|
-
|
|
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
|
|
1882
|
-
data
|
|
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
|
|
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 {
|
|
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
|
|
2116
|
+
const result = { data: null, error: null };
|
|
1910
2117
|
if (!email) {
|
|
1911
|
-
|
|
1912
|
-
return
|
|
2118
|
+
result.error = "No email provided.";
|
|
2119
|
+
return result;
|
|
1913
2120
|
}
|
|
1914
2121
|
if (!listName) {
|
|
1915
|
-
|
|
1916
|
-
return
|
|
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
|
-
|
|
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
|
|
1928
|
-
data
|
|
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
|
|
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 {
|
|
2149
|
+
* const { data, error } = await mailchannels.users.listEntries('name@example.com', 'safelist')
|
|
1943
2150
|
* ```
|
|
1944
2151
|
*/
|
|
1945
2152
|
async listEntries(email, listName) {
|
|
1946
|
-
const
|
|
2153
|
+
const result = { data: null, error: null };
|
|
1947
2154
|
if (!email) {
|
|
1948
|
-
|
|
1949
|
-
return
|
|
2155
|
+
result.error = "No email provided.";
|
|
2156
|
+
return result;
|
|
1950
2157
|
}
|
|
1951
2158
|
if (!listName) {
|
|
1952
|
-
|
|
1953
|
-
return
|
|
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
|
-
|
|
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
|
|
1964
|
-
data
|
|
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
|
|
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
|
|
2193
|
+
const result = { success: false, error: null };
|
|
1987
2194
|
if (!email) {
|
|
1988
|
-
|
|
1989
|
-
return
|
|
2195
|
+
result.error = "No email provided.";
|
|
2196
|
+
return result;
|
|
1990
2197
|
}
|
|
1991
2198
|
if (!listName) {
|
|
1992
|
-
|
|
1993
|
-
return
|
|
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
|
-
|
|
2207
|
+
result.success = true;
|
|
2001
2208
|
return;
|
|
2002
2209
|
}
|
|
2003
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
2238
|
+
result.success = true;
|
|
2032
2239
|
return;
|
|
2033
2240
|
}
|
|
2034
|
-
|
|
2241
|
+
result.error = getStatusError(response);
|
|
2035
2242
|
}
|
|
2036
2243
|
});
|
|
2037
|
-
return
|
|
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 {
|
|
2251
|
+
* const { data, error } = await mailchannels.service.subscriptions()
|
|
2045
2252
|
* ```
|
|
2046
2253
|
*/
|
|
2047
2254
|
async subscriptions() {
|
|
2048
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
2057
|
-
|
|
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
|
|
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
|
-
|
|
2293
|
+
result.success = true;
|
|
2081
2294
|
return;
|
|
2082
2295
|
}
|
|
2083
|
-
|
|
2296
|
+
result.error = getStatusError(response);
|
|
2084
2297
|
}
|
|
2085
2298
|
});
|
|
2086
|
-
return
|
|
2299
|
+
return result;
|
|
2087
2300
|
}
|
|
2088
2301
|
}
|
|
2089
2302
|
|