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