n8n-nodes-resend 2.6.1 → 2.6.3

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.
Files changed (44) hide show
  1. package/dist/credentials/ResendApi.credentials.d.ts +2 -2
  2. package/dist/credentials/ResendOAuth2Api.credentials.d.ts +2 -2
  3. package/dist/credentials/ResendWebhookSigningSecretApi.credentials.d.ts +1 -1
  4. package/dist/nodes/Resend/Resend.node.d.ts +2 -2
  5. package/dist/nodes/Resend/Resend.node.js +4 -4
  6. package/dist/nodes/Resend/ResendTrigger.node.d.ts +13 -1
  7. package/dist/nodes/Resend/ResendTrigger.node.js +111 -6
  8. package/dist/nodes/Resend/actions/account/disconnect.operation.js +1 -1
  9. package/dist/nodes/Resend/actions/account/index.js +1 -1
  10. package/dist/nodes/Resend/actions/domain/claim.operation.js +80 -93
  11. package/dist/nodes/Resend/actions/domain/create.operation.js +120 -133
  12. package/dist/nodes/Resend/actions/domain/createTrackingDomain.operation.js +24 -37
  13. package/dist/nodes/Resend/actions/domain/deleteTrackingDomain.operation.js +24 -37
  14. package/dist/nodes/Resend/actions/domain/getTrackingDomain.operation.js +24 -37
  15. package/dist/nodes/Resend/actions/domain/listTrackingDomains.operation.js +3 -12
  16. package/dist/nodes/Resend/actions/domain/verifyClaim.operation.js +1 -10
  17. package/dist/nodes/Resend/actions/domain/verifyTrackingDomain.operation.js +24 -37
  18. package/dist/nodes/Resend/actions/email/send.operation.js +1 -11
  19. package/dist/nodes/Resend/actions/email/sendAndWait.operation.d.ts +1 -1
  20. package/dist/nodes/Resend/actions/email/sendAndWait.operation.js +1 -1
  21. package/dist/nodes/Resend/actions/email/sendBatch.operation.js +17 -4
  22. package/dist/nodes/Resend/actions/event/create.operation.js +23 -36
  23. package/dist/nodes/Resend/actions/event/delete.operation.js +0 -9
  24. package/dist/nodes/Resend/actions/event/get.operation.js +0 -9
  25. package/dist/nodes/Resend/actions/event/list.operation.js +1 -10
  26. package/dist/nodes/Resend/actions/event/send.operation.js +0 -10
  27. package/dist/nodes/Resend/actions/event/update.operation.js +25 -38
  28. package/dist/nodes/Resend/actions/router.js +2 -1
  29. package/dist/nodes/Resend/actions/workflow/create.operation.js +0 -10
  30. package/dist/nodes/Resend/actions/workflow/delete.operation.js +0 -9
  31. package/dist/nodes/Resend/actions/workflow/get.operation.js +0 -9
  32. package/dist/nodes/Resend/actions/workflow/getRun.operation.js +25 -38
  33. package/dist/nodes/Resend/actions/workflow/getRunStep.operation.js +0 -10
  34. package/dist/nodes/Resend/actions/workflow/list.operation.js +1 -10
  35. package/dist/nodes/Resend/actions/workflow/listRunSteps.operation.js +27 -40
  36. package/dist/nodes/Resend/actions/workflow/listRuns.operation.js +2 -11
  37. package/dist/nodes/Resend/actions/workflow/update.operation.js +31 -44
  38. package/dist/nodes/Resend/methods/index.js +6 -6
  39. package/dist/nodes/Resend/transport/index.d.ts +3 -2
  40. package/dist/nodes/Resend/transport/index.js +5 -1
  41. package/dist/nodes/Resend/utils/dynamicFields.d.ts +20 -20
  42. package/dist/nodes/Resend/utils/sendAndWait/utils.d.ts +2 -2
  43. package/dist/nodes/Resend/utils/sendAndWait/utils.js +19 -15
  44. package/package.json +5 -2
@@ -5,8 +5,8 @@ declare class ResendApi implements ICredentialType {
5
5
  displayName: string;
6
6
  documentationUrl: string;
7
7
  icon: {
8
- readonly light: 'file:resend-icon-black.svg';
9
- readonly dark: 'file:resend-icon-white.svg';
8
+ readonly light: "file:resend-icon-black.svg";
9
+ readonly dark: "file:resend-icon-white.svg";
10
10
  };
11
11
  properties: INodeProperties[];
12
12
  authenticate: IAuthenticateGeneric;
@@ -6,8 +6,8 @@ declare class ResendOAuth2Api implements ICredentialType {
6
6
  displayName: string;
7
7
  documentationUrl: string;
8
8
  icon: {
9
- readonly light: 'file:resend-icon-black.svg';
10
- readonly dark: 'file:resend-icon-white.svg';
9
+ readonly light: "file:resend-icon-black.svg";
10
+ readonly dark: "file:resend-icon-white.svg";
11
11
  };
12
12
  properties: INodeProperties[];
13
13
  }
@@ -4,7 +4,7 @@ declare class ResendWebhookSigningSecretApi implements ICredentialType {
4
4
  name: string;
5
5
  displayName: string;
6
6
  documentationUrl: string;
7
- icon: 'file:resend.svg';
7
+ icon: "file:resend.svg";
8
8
  properties: INodeProperties[];
9
9
  }
10
10
  //#endregion
@@ -37,7 +37,6 @@ declare class Resend implements INodeType {
37
37
  webhookResponse?: undefined;
38
38
  workflowData?: undefined;
39
39
  } | {
40
- noWebhookResponse?: undefined;
41
40
  webhookResponse: undefined;
42
41
  workflowData: {
43
42
  json: {
@@ -46,8 +45,8 @@ declare class Resend implements INodeType {
46
45
  };
47
46
  };
48
47
  }[][];
49
- } | {
50
48
  noWebhookResponse?: undefined;
49
+ } | {
51
50
  webhookResponse: undefined;
52
51
  workflowData: {
53
52
  json: {
@@ -56,6 +55,7 @@ declare class Resend implements INodeType {
56
55
  };
57
56
  };
58
57
  }[][];
58
+ noWebhookResponse?: undefined;
59
59
  }>;
60
60
  execute: typeof router;
61
61
  }
@@ -54,13 +54,13 @@ var Resend = class {
54
54
  name: "authentication",
55
55
  type: "options",
56
56
  options: [{
57
- name: "OAuth2 (Recommended)",
58
- value: "oAuth2",
59
- description: "Sign in with your Resend account — n8n registers itself as an OAuth client automatically, no API key needed"
60
- }, {
61
57
  name: "API Key",
62
58
  value: "apiKey",
63
59
  description: "Manually paste a Resend API key"
60
+ }, {
61
+ name: "OAuth2 (Recommended)",
62
+ value: "oAuth2",
63
+ description: "Sign in with your Resend account — n8n registers itself as an OAuth client automatically, no API key needed"
64
64
  }],
65
65
  default: "oAuth2"
66
66
  },
@@ -1,7 +1,19 @@
1
- import { INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from "n8n-workflow";
1
+ import { ICredentialTestFunctions, ICredentialsDecrypted, IHookFunctions, INodeCredentialTestResult, INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from "n8n-workflow";
2
2
  //#region nodes/Resend/ResendTrigger.node.d.ts
3
3
  declare class ResendTrigger implements INodeType {
4
4
  description: INodeTypeDescription;
5
+ methods: {
6
+ credentialTest: {
7
+ resendWebhookSigningSecretTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<INodeCredentialTestResult>;
8
+ };
9
+ };
10
+ webhookMethods: {
11
+ default: {
12
+ checkExists(this: IHookFunctions): Promise<boolean>;
13
+ create(this: IHookFunctions): Promise<boolean>;
14
+ delete(this: IHookFunctions): Promise<boolean>;
15
+ };
16
+ };
5
17
  webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
6
18
  }
7
19
  //#endregion
@@ -32,6 +32,31 @@ async function verifySvixSignature(payload, svixId, svixTimestamp, svixSignature
32
32
  }
33
33
  throw new n8n_workflow.NodeOperationError(node, "Invalid webhook signature");
34
34
  }
35
+ async function resendApiRequest(method, endpoint, body) {
36
+ return await this.helpers.httpRequestWithAuthentication.call(this, "resendApi", {
37
+ url: `https://api.resend.com${endpoint}`,
38
+ method,
39
+ headers: {
40
+ "Content-Type": "application/json",
41
+ "User-Agent": "n8n-nodes-resend"
42
+ },
43
+ json: true,
44
+ ...body ? { body } : {}
45
+ });
46
+ }
47
+ function isNotFoundError(error) {
48
+ var _ref, _e$httpCode, _e$response;
49
+ const e = error;
50
+ const code = (_ref = (_e$httpCode = e === null || e === void 0 ? void 0 : e.httpCode) !== null && _e$httpCode !== void 0 ? _e$httpCode : e === null || e === void 0 ? void 0 : e.statusCode) !== null && _ref !== void 0 ? _ref : e === null || e === void 0 || (_e$response = e.response) === null || _e$response === void 0 ? void 0 : _e$response.status;
51
+ return String(code) === "404";
52
+ }
53
+ async function hasResendApiCredential(context) {
54
+ try {
55
+ return Boolean(await context.getCredentials("resendApi"));
56
+ } catch {
57
+ return false;
58
+ }
59
+ }
35
60
  var ResendTrigger = class {
36
61
  constructor() {
37
62
  this.description = {
@@ -48,10 +73,14 @@ var ResendTrigger = class {
48
73
  defaults: { name: "Resend Trigger" },
49
74
  credentials: [{
50
75
  name: "resendWebhookSigningSecretApi",
51
- required: true
76
+ required: false,
77
+ testedBy: "resendWebhookSigningSecretTest"
78
+ }, {
79
+ name: "resendApi",
80
+ required: false
52
81
  }],
53
82
  triggerPanel: {
54
- header: "Copy the webhook URL below and paste it into your Resend dashboard webhook configuration.",
83
+ header: "Add a Resend API credential to register this webhook automatically, or copy the webhook URL below and paste it into your Resend dashboard webhook configuration.",
55
84
  executionsHelp: {
56
85
  inactive: "Webhooks have two modes: test and production.<br><br><b>Use test mode while you build your workflow</b>. Click the \"Listen for test event\" button, then paste the test URL into your Resend webhook configuration. The webhook executions will show up in the editor.<br><br><b>Use production mode to run your workflow automatically</b>. Activate the workflow, then paste the production URL into your Resend webhook configuration. These executions will show up in the executions list, but not in the editor.",
57
86
  active: "Webhooks have two modes: test and production.<br><br><b>Use test mode while you build your workflow</b>. Click the \"Listen for test event\" button, then paste the test URL into your Resend webhook configuration. The webhook executions will show up in the editor.<br><br><b>Use production mode to run your workflow automatically</b>. Since the workflow is activated, you can paste the production URL into your Resend webhook configuration. These executions will show up in the executions list, but not in the editor."
@@ -152,16 +181,92 @@ var ResendTrigger = class {
152
181
  }
153
182
  ],
154
183
  description: "Select the Resend event types to listen for"
155
- }]
184
+ }],
185
+ usableAsTool: true
156
186
  };
187
+ this.methods = { credentialTest: { async resendWebhookSigningSecretTest(credential) {
188
+ var _credential$data;
189
+ const secret = typeof ((_credential$data = credential.data) === null || _credential$data === void 0 ? void 0 : _credential$data.webhookSigningSecret) === "string" ? credential.data.webhookSigningSecret.trim() : "";
190
+ if (!secret.startsWith("whsec_")) return {
191
+ status: "Error",
192
+ message: "Signing secret must start with \"whsec_\". Copy it from the webhook settings page in your Resend dashboard."
193
+ };
194
+ const encoded = secret.slice(6);
195
+ if (!encoded || Buffer.from(encoded, "base64").length === 0) return {
196
+ status: "Error",
197
+ message: "Signing secret is not valid base64"
198
+ };
199
+ return {
200
+ status: "OK",
201
+ message: "Signing secret format is valid"
202
+ };
203
+ } } };
204
+ this.webhookMethods = { default: {
205
+ async checkExists() {
206
+ if (!await hasResendApiCredential(this)) return true;
207
+ const webhookData = this.getWorkflowStaticData("node");
208
+ if (!webhookData.webhookId) return false;
209
+ try {
210
+ await resendApiRequest.call(this, "GET", `/webhooks/${webhookData.webhookId}`);
211
+ return true;
212
+ } catch (error) {
213
+ if (isNotFoundError(error)) {
214
+ delete webhookData.webhookId;
215
+ delete webhookData.webhookSigningSecret;
216
+ return false;
217
+ }
218
+ throw new n8n_workflow.NodeApiError(this.getNode(), error);
219
+ }
220
+ },
221
+ async create() {
222
+ var _ref2, _response$data, _ref3, _response$data2;
223
+ if (!await hasResendApiCredential(this)) return true;
224
+ const webhookUrl = this.getNodeWebhookUrl("default");
225
+ const events = this.getNodeParameter("events");
226
+ const response = await resendApiRequest.call(this, "POST", "/webhooks", {
227
+ endpoint: webhookUrl,
228
+ events
229
+ });
230
+ const webhookId = (_ref2 = response.id) !== null && _ref2 !== void 0 ? _ref2 : (_response$data = response.data) === null || _response$data === void 0 ? void 0 : _response$data.id;
231
+ if (!webhookId) throw new n8n_workflow.NodeOperationError(this.getNode(), "Resend did not return a webhook ID when creating the webhook");
232
+ const signingSecret = (_ref3 = response.signing_secret) !== null && _ref3 !== void 0 ? _ref3 : (_response$data2 = response.data) === null || _response$data2 === void 0 ? void 0 : _response$data2.signing_secret;
233
+ const webhookData = this.getWorkflowStaticData("node");
234
+ webhookData.webhookId = webhookId;
235
+ if (signingSecret) webhookData.webhookSigningSecret = signingSecret;
236
+ return true;
237
+ },
238
+ async delete() {
239
+ const webhookData = this.getWorkflowStaticData("node");
240
+ if (!webhookData.webhookId) return true;
241
+ if (await hasResendApiCredential(this)) try {
242
+ await resendApiRequest.call(this, "DELETE", `/webhooks/${webhookData.webhookId}`);
243
+ } catch (error) {
244
+ if (!isNotFoundError(error)) throw new n8n_workflow.NodeApiError(this.getNode(), error);
245
+ }
246
+ delete webhookData.webhookId;
247
+ delete webhookData.webhookSigningSecret;
248
+ return true;
249
+ }
250
+ } };
157
251
  }
158
252
  async webhook() {
159
253
  const bodyData = this.getBodyData();
160
254
  const headers = this.getHeaderData();
161
255
  const request = this.getRequestObject();
162
256
  const subscribedEvents = this.getNodeParameter("events");
163
- const webhookSigningSecret = (await this.getCredentials("resendWebhookSigningSecretApi")).webhookSigningSecret;
164
- if (webhookSigningSecret && webhookSigningSecret.trim() !== "") try {
257
+ const webhookData = this.getWorkflowStaticData("node");
258
+ let webhookSigningSecret = typeof webhookData.webhookSigningSecret === "string" ? webhookData.webhookSigningSecret : "";
259
+ if (!webhookSigningSecret) try {
260
+ var _ref4;
261
+ webhookSigningSecret = (_ref4 = (await this.getCredentials("resendWebhookSigningSecretApi")).webhookSigningSecret) !== null && _ref4 !== void 0 ? _ref4 : "";
262
+ } catch {
263
+ webhookSigningSecret = "";
264
+ }
265
+ if (!webhookSigningSecret || webhookSigningSecret.trim() === "") {
266
+ this.getResponseObject().status(401).json({ error: "Webhook signing secret is not configured" });
267
+ return { noWebhookResponse: true };
268
+ }
269
+ try {
165
270
  var _rawBody;
166
271
  const rawBody = (_rawBody = request.rawBody) !== null && _rawBody !== void 0 ? _rawBody : request.body;
167
272
  const payload = typeof rawBody === "string" ? rawBody : Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : JSON.stringify(bodyData !== null && bodyData !== void 0 ? bodyData : {});
@@ -173,7 +278,7 @@ var ResendTrigger = class {
173
278
  return { noWebhookResponse: true };
174
279
  }
175
280
  await verifySvixSignature(payload, svixId, svixTimestamp, svixSignature, webhookSigningSecret, this.getNode());
176
- } catch (_error) {
281
+ } catch {
177
282
  this.getResponseObject().status(401).json({ error: "Invalid webhook signature" });
178
283
  return { noWebhookResponse: true };
179
284
  }
@@ -16,7 +16,7 @@ async function execute(index) {
16
16
  const refreshToken = (_credentials$oauthTok = credentials.oauthTokenData) === null || _credentials$oauthTok === void 0 ? void 0 : _credentials$oauthTok.refresh_token;
17
17
  if (!clientId || !refreshToken) throw new n8n_workflow.NodeOperationError(this.getNode(), "No active Resend OAuth connection was found to disconnect. Connect the credential first.", { itemIndex: index });
18
18
  try {
19
- await this.helpers.httpRequest({
19
+ await this.helpers.httpRequestWithAuthentication.call(this, "resendOAuth2Api", {
20
20
  method: "POST",
21
21
  url: "https://api.resend.com/oauth/revoke",
22
22
  headers: {
@@ -19,7 +19,7 @@ const operations = [{
19
19
  name: "Disconnect",
20
20
  value: "disconnect",
21
21
  description: "Revoke the connected Resend OAuth2 grant, ending the connection to your Resend account",
22
- action: "Disconnect the Resend account"
22
+ action: "Disconnect the resend account"
23
23
  }],
24
24
  default: "disconnect"
25
25
  }];
@@ -6,99 +6,86 @@ var claim_operation_exports = /* @__PURE__ */ require_runtime.__exportAll({
6
6
  description: () => description,
7
7
  execute: () => execute
8
8
  });
9
- const description = [
10
- {
11
- displayName: "Use this operation when creating a domain fails because the domain is already verified by another team. Resend creates a placeholder domain and returns a TXT record to add to your DNS.",
12
- name: "claimDomainNotice",
13
- type: "notice",
14
- default: "",
15
- displayOptions: { show: {
16
- resource: ["domains"],
17
- operation: ["claim"]
18
- } }
19
- },
20
- {
21
- displayName: "Domain Name",
22
- name: "domainName",
23
- type: "string",
24
- required: true,
25
- default: "",
26
- placeholder: "example.com",
27
- displayOptions: { show: {
28
- resource: ["domains"],
29
- operation: ["claim"]
30
- } },
31
- description: "The name of the domain you want to claim"
32
- },
33
- {
34
- displayName: "Additional Options",
35
- name: "additionalOptions",
36
- type: "collection",
37
- placeholder: "Add Option",
38
- default: {},
39
- displayOptions: { show: {
40
- resource: ["domains"],
41
- operation: ["claim"]
42
- } },
43
- options: [
44
- {
45
- displayName: "Click Tracking",
46
- name: "click_tracking",
47
- type: "boolean",
48
- default: false,
49
- description: "Whether to track clicks within the body of each HTML email. Only applied if a tracking_subdomain is configured and verified."
50
- },
51
- {
52
- displayName: "Custom Return Path",
53
- name: "custom_return_path",
54
- type: "string",
55
- default: "send",
56
- placeholder: "send",
57
- description: "Choose a subdomain for the Return-Path address. Defaults to \"send\" (i.e., send.yourdomain.tld)."
58
- },
59
- {
60
- displayName: "Open Tracking",
61
- name: "open_tracking",
62
- type: "boolean",
63
- default: false,
64
- description: "Whether to track the open rate of each email. Only applied if a tracking_subdomain is configured and verified."
65
- },
66
- {
67
- displayName: "Region",
68
- name: "region",
69
- type: "options",
70
- options: [
71
- {
72
- name: "US East (N. Virginia)",
73
- value: "us-east-1"
74
- },
75
- {
76
- name: "EU West (Ireland)",
77
- value: "eu-west-1"
78
- },
79
- {
80
- name: "South America (São Paulo)",
81
- value: "sa-east-1"
82
- },
83
- {
84
- name: "Asia Pacific (Tokyo)",
85
- value: "ap-northeast-1"
86
- }
87
- ],
88
- default: "us-east-1",
89
- description: "The region where emails will be sent from"
90
- },
91
- {
92
- displayName: "Tracking Subdomain",
93
- name: "tracking_subdomain",
94
- type: "string",
95
- default: "",
96
- placeholder: "links",
97
- description: "Configure a custom subdomain for click and open tracking (e.g., \"links\" on domain example.com produces links.example.com)"
98
- }
99
- ]
100
- }
101
- ];
9
+ const description = [{
10
+ displayName: "Domain Name",
11
+ name: "domainName",
12
+ type: "string",
13
+ required: true,
14
+ default: "",
15
+ placeholder: "example.com",
16
+ displayOptions: { show: {
17
+ resource: ["domains"],
18
+ operation: ["claim"]
19
+ } },
20
+ description: "The name of the domain you want to claim"
21
+ }, {
22
+ displayName: "Additional Options",
23
+ name: "additionalOptions",
24
+ type: "collection",
25
+ placeholder: "Add Option",
26
+ default: {},
27
+ displayOptions: { show: {
28
+ resource: ["domains"],
29
+ operation: ["claim"]
30
+ } },
31
+ options: [
32
+ {
33
+ displayName: "Click Tracking",
34
+ name: "click_tracking",
35
+ type: "boolean",
36
+ default: false,
37
+ description: "Whether to track clicks within the body of each HTML email. Only applied if a tracking_subdomain is configured and verified."
38
+ },
39
+ {
40
+ displayName: "Custom Return Path",
41
+ name: "custom_return_path",
42
+ type: "string",
43
+ default: "send",
44
+ placeholder: "send",
45
+ description: "Choose a subdomain for the Return-Path address. Defaults to \"send\" (i.e., send.yourdomain.tld)."
46
+ },
47
+ {
48
+ displayName: "Open Tracking",
49
+ name: "open_tracking",
50
+ type: "boolean",
51
+ default: false,
52
+ description: "Whether to track the open rate of each email. Only applied if a tracking_subdomain is configured and verified."
53
+ },
54
+ {
55
+ displayName: "Region",
56
+ name: "region",
57
+ type: "options",
58
+ options: [
59
+ {
60
+ name: "US East (N. Virginia)",
61
+ value: "us-east-1"
62
+ },
63
+ {
64
+ name: "EU West (Ireland)",
65
+ value: "eu-west-1"
66
+ },
67
+ {
68
+ name: "South America (São Paulo)",
69
+ value: "sa-east-1"
70
+ },
71
+ {
72
+ name: "Asia Pacific (Tokyo)",
73
+ value: "ap-northeast-1"
74
+ }
75
+ ],
76
+ default: "us-east-1",
77
+ description: "The region where emails will be sent from"
78
+ },
79
+ {
80
+ displayName: "Tracking Subdomain",
81
+ name: "tracking_subdomain",
82
+ type: "string",
83
+ default: "",
84
+ placeholder: "links",
85
+ description: "Configure a custom subdomain for click and open tracking (e.g., \"links\" on domain example.com produces links.example.com)"
86
+ }
87
+ ]
88
+ }];
102
89
  async function execute(index) {
103
90
  const domainName = this.getNodeParameter("domainName", index);
104
91
  const additionalOptions = this.getNodeParameter("additionalOptions", index);