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