n8n-nodes-resend 2.6.2 → 2.6.4
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/credentials/ResendApi.credentials.d.ts +2 -2
- package/dist/credentials/ResendOAuth2Api.credentials.d.ts +2 -2
- package/dist/credentials/ResendWebhookSigningSecretApi.credentials.d.ts +1 -1
- package/dist/nodes/Resend/Resend.node.d.ts +5 -3
- package/dist/nodes/Resend/Resend.node.js +14 -5
- package/dist/nodes/Resend/ResendTrigger.node.d.ts +13 -1
- package/dist/nodes/Resend/ResendTrigger.node.js +111 -6
- package/dist/nodes/Resend/actions/account/index.js +1 -1
- package/dist/nodes/Resend/actions/domain/listTrackingDomains.operation.js +2 -2
- package/dist/nodes/Resend/actions/email/send.operation.js +1 -1
- package/dist/nodes/Resend/actions/email/sendAndWait.operation.d.ts +1 -1
- package/dist/nodes/Resend/actions/email/sendAndWait.operation.js +1 -1
- package/dist/nodes/Resend/actions/email/sendBatch.operation.js +17 -4
- package/dist/nodes/Resend/actions/router.js +4 -1
- package/dist/nodes/Resend/actions/suppression/batchAdd.operation.d.ts +8 -0
- package/dist/nodes/Resend/actions/suppression/batchAdd.operation.js +41 -0
- package/dist/nodes/Resend/actions/suppression/batchRemove.operation.d.ts +8 -0
- package/dist/nodes/Resend/actions/suppression/batchRemove.operation.js +80 -0
- package/dist/nodes/Resend/actions/suppression/create.operation.d.ts +8 -0
- package/dist/nodes/Resend/actions/suppression/create.operation.js +37 -0
- package/dist/nodes/Resend/actions/suppression/delete.operation.d.ts +8 -0
- package/dist/nodes/Resend/actions/suppression/delete.operation.js +37 -0
- package/dist/nodes/Resend/actions/suppression/execute.d.ts +4 -0
- package/dist/nodes/Resend/actions/suppression/execute.js +18 -0
- package/dist/nodes/Resend/actions/suppression/get.operation.d.ts +8 -0
- package/dist/nodes/Resend/actions/suppression/get.operation.js +37 -0
- package/dist/nodes/Resend/actions/suppression/index.d.ts +13 -0
- package/dist/nodes/Resend/actions/suppression/index.js +122 -0
- package/dist/nodes/Resend/actions/suppression/list.operation.d.ts +8 -0
- package/dist/nodes/Resend/actions/suppression/list.operation.js +83 -0
- package/dist/nodes/Resend/actions/workflow/listRunSteps.operation.js +2 -2
- package/dist/nodes/Resend/actions/workflow/listRuns.operation.js +2 -2
- package/dist/nodes/Resend/methods/index.d.ts +3 -1
- package/dist/nodes/Resend/methods/index.js +35 -33
- package/dist/nodes/Resend/transport/index.d.ts +3 -2
- package/dist/nodes/Resend/transport/index.js +9 -3
- package/dist/nodes/Resend/utils/dynamicFields.d.ts +22 -20
- package/dist/nodes/Resend/utils/dynamicFields.js +2 -61
- package/dist/nodes/Resend/utils/sendAndWait/email-templates.d.ts +1 -3
- package/dist/nodes/Resend/utils/sendAndWait/email-templates.js +0 -10
- package/dist/nodes/Resend/utils/sendAndWait/index.d.ts +2 -2
- package/dist/nodes/Resend/utils/sendAndWait/index.js +0 -2
- package/dist/nodes/Resend/utils/sendAndWait/utils.d.ts +2 -2
- package/dist/nodes/Resend/utils/sendAndWait/utils.js +19 -15
- package/package.json +10 -5
package/README.md
CHANGED
|
@@ -45,6 +45,7 @@ The table below shows which endpoints are currently implemented:
|
|
|
45
45
|
| **Contacts** | `/contacts` | ✅ Full | Create, List, Get, Update, Delete, Add to Segment, List Segments, Remove from Segment, Get Topics, Update Topics |
|
|
46
46
|
| **Broadcasts** | `/broadcasts` | ✅ Full | Create, List, Get, Update, Delete, Send |
|
|
47
47
|
| **Segments** | `/segments` | ✅ Full | Create, List, Get, Delete |
|
|
48
|
+
| **Suppressions** | `/suppressions` | ✅ Full | Create, List, Get, Delete, Batch Add, Batch Remove |
|
|
48
49
|
| **Topics** | `/topics` | ✅ Full | Create, List, Get, Update, Delete |
|
|
49
50
|
| **Contact Properties** | `/contact-properties` | ✅ Full | Create, List, Get, Update, Delete |
|
|
50
51
|
| **Webhooks** | `/webhooks` | ✅ Full | Create, List, Get, Update, Delete |
|
|
@@ -5,8 +5,8 @@ declare class ResendApi implements ICredentialType {
|
|
|
5
5
|
displayName: string;
|
|
6
6
|
documentationUrl: string;
|
|
7
7
|
icon: {
|
|
8
|
-
readonly light:
|
|
9
|
-
readonly dark:
|
|
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:
|
|
10
|
-
readonly dark:
|
|
9
|
+
readonly light: "file:resend-icon-black.svg";
|
|
10
|
+
readonly dark: "file:resend-icon-white.svg";
|
|
11
11
|
};
|
|
12
12
|
properties: INodeProperties[];
|
|
13
13
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { router } from "./actions/router.js";
|
|
2
|
-
import { getBroadcasts, getContactProperties, getContacts, getDomains, getEmails, getReceivedEmails, getSegments, getTemplateVariables, getTemplates, getTopics, getWebhooks } from "./methods/index.js";
|
|
2
|
+
import { getBroadcasts, getContactProperties, getContacts, getDomains, getEmails, getReceivedEmails, getSegments, getSuppressions, getTemplateVariables, getTemplates, getTopics, getWebhooks } from "./methods/index.js";
|
|
3
3
|
import { INodeType, INodeTypeDescription, IWebhookFunctions } from "n8n-workflow";
|
|
4
4
|
//#region nodes/Resend/Resend.node.d.ts
|
|
5
5
|
declare class Resend implements INodeType {
|
|
@@ -13,6 +13,7 @@ declare class Resend implements INodeType {
|
|
|
13
13
|
getEmails: typeof getEmails;
|
|
14
14
|
getReceivedEmails: typeof getReceivedEmails;
|
|
15
15
|
getSegments: typeof getSegments;
|
|
16
|
+
getSuppressions: typeof getSuppressions;
|
|
16
17
|
getTemplateVariables: typeof getTemplateVariables;
|
|
17
18
|
getTemplates: typeof getTemplates;
|
|
18
19
|
getTopics: typeof getTopics;
|
|
@@ -26,6 +27,7 @@ declare class Resend implements INodeType {
|
|
|
26
27
|
getEmails: (this: import("n8n-workflow").ILoadOptionsFunctions, filter?: string) => Promise<import("n8n-workflow").INodeListSearchResult>;
|
|
27
28
|
getReceivedEmails: (this: import("n8n-workflow").ILoadOptionsFunctions, filter?: string) => Promise<import("n8n-workflow").INodeListSearchResult>;
|
|
28
29
|
getSegments: (this: import("n8n-workflow").ILoadOptionsFunctions, filter?: string) => Promise<import("n8n-workflow").INodeListSearchResult>;
|
|
30
|
+
getSuppressions: (this: import("n8n-workflow").ILoadOptionsFunctions, filter?: string) => Promise<import("n8n-workflow").INodeListSearchResult>;
|
|
29
31
|
getTemplates: (this: import("n8n-workflow").ILoadOptionsFunctions, filter?: string) => Promise<import("n8n-workflow").INodeListSearchResult>;
|
|
30
32
|
getTemplateVariables: (this: import("n8n-workflow").ILoadOptionsFunctions, filter?: string) => Promise<import("n8n-workflow").INodeListSearchResult>;
|
|
31
33
|
getTopics: (this: import("n8n-workflow").ILoadOptionsFunctions, filter?: string) => Promise<import("n8n-workflow").INodeListSearchResult>;
|
|
@@ -37,7 +39,6 @@ declare class Resend implements INodeType {
|
|
|
37
39
|
webhookResponse?: undefined;
|
|
38
40
|
workflowData?: undefined;
|
|
39
41
|
} | {
|
|
40
|
-
noWebhookResponse?: undefined;
|
|
41
42
|
webhookResponse: undefined;
|
|
42
43
|
workflowData: {
|
|
43
44
|
json: {
|
|
@@ -46,8 +47,8 @@ declare class Resend implements INodeType {
|
|
|
46
47
|
};
|
|
47
48
|
};
|
|
48
49
|
}[][];
|
|
49
|
-
} | {
|
|
50
50
|
noWebhookResponse?: undefined;
|
|
51
|
+
} | {
|
|
51
52
|
webhookResponse: undefined;
|
|
52
53
|
workflowData: {
|
|
53
54
|
json: {
|
|
@@ -56,6 +57,7 @@ declare class Resend implements INodeType {
|
|
|
56
57
|
};
|
|
57
58
|
};
|
|
58
59
|
}[][];
|
|
60
|
+
noWebhookResponse?: undefined;
|
|
59
61
|
}>;
|
|
60
62
|
execute: typeof router;
|
|
61
63
|
}
|
|
@@ -12,6 +12,7 @@ const require_nodes_Resend_actions_event_index = require("./actions/event/index.
|
|
|
12
12
|
const require_nodes_Resend_actions_log_index = require("./actions/log/index.js");
|
|
13
13
|
const require_nodes_Resend_actions_receivingEmail_index = require("./actions/receivingEmail/index.js");
|
|
14
14
|
const require_nodes_Resend_actions_segment_index = require("./actions/segment/index.js");
|
|
15
|
+
const require_nodes_Resend_actions_suppression_index = require("./actions/suppression/index.js");
|
|
15
16
|
const require_nodes_Resend_actions_template_index = require("./actions/template/index.js");
|
|
16
17
|
const require_nodes_Resend_actions_topic_index = require("./actions/topic/index.js");
|
|
17
18
|
const require_nodes_Resend_actions_webhook_index = require("./actions/webhook/index.js");
|
|
@@ -33,7 +34,7 @@ var Resend = class {
|
|
|
33
34
|
version: 1,
|
|
34
35
|
usableAsTool: true,
|
|
35
36
|
description: "Send emails, manage contacts, create broadcasts, handle templates, domains, segments, topics, and webhooks using the Resend email platform",
|
|
36
|
-
subtitle: "={{(() => { const resourceLabels = { broadcasts: \"broadcast\", contacts: \"contact\", contactProperties: \"contact property\", domains: \"domain\", email: \"email\", logs: \"log\", receivingEmails: \"received email\", workflows: \"workflow\", events: \"event\", segments: \"segment\", templates: \"template\", topics: \"topic\", webhooks: \"webhook\" }; const operationLabels = { retrieve: \"get\", sendBatch: \"send batch\", listAttachments: \"list attachments\", getAttachment: \"get attachment\", addToSegment: \"add to segment\", listSegments: \"list segments\", removeFromSegment: \"remove from segment\", getTopics: \"get topics\", updateTopics: \"update topics\", listRuns: \"list runs\", getRun: \"get run\", listRunSteps: \"list run steps\", getRunStep: \"get run step\" }; const resource = $parameter[\"resource\"]; const operation = $parameter[\"operation\"]; const resourceLabel = resourceLabels[resource] ?? resource; const operationLabel = operationLabels[operation] ?? operation; return operationLabel + \": \" + resourceLabel; })() }}",
|
|
37
|
+
subtitle: "={{(() => { const resourceLabels = { account: \"account\", broadcasts: \"broadcast\", contacts: \"contact\", contactProperties: \"contact property\", domains: \"domain\", email: \"email\", logs: \"log\", receivingEmails: \"received email\", workflows: \"workflow\", events: \"event\", segments: \"segment\", suppressions: \"suppression\", templates: \"template\", topics: \"topic\", webhooks: \"webhook\" }; const operationLabels = { retrieve: \"get\", sendBatch: \"send batch\", listAttachments: \"list attachments\", getAttachment: \"get attachment\", addToSegment: \"add to segment\", listSegments: \"list segments\", removeFromSegment: \"remove from segment\", getTopics: \"get topics\", updateTopics: \"update topics\", listRuns: \"list runs\", getRun: \"get run\", listRunSteps: \"list run steps\", getRunStep: \"get run step\", batchAdd: \"batch add\", batchRemove: \"batch remove\" }; const resource = $parameter[\"resource\"]; const operation = $parameter[\"operation\"]; const resourceLabel = resourceLabels[resource] ?? resource; const operationLabel = operationLabels[operation] ?? operation; return operationLabel + \": \" + resourceLabel; })() }}",
|
|
37
38
|
defaults: { name: "Resend" },
|
|
38
39
|
credentials: [{
|
|
39
40
|
name: "resendOAuth2Api",
|
|
@@ -54,13 +55,13 @@ var Resend = class {
|
|
|
54
55
|
name: "authentication",
|
|
55
56
|
type: "options",
|
|
56
57
|
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
58
|
name: "API Key",
|
|
62
59
|
value: "apiKey",
|
|
63
60
|
description: "Manually paste a Resend API key"
|
|
61
|
+
}, {
|
|
62
|
+
name: "OAuth2 (Recommended)",
|
|
63
|
+
value: "oAuth2",
|
|
64
|
+
description: "Sign in with your Resend account — n8n registers itself as an OAuth client automatically, no API key needed"
|
|
64
65
|
}],
|
|
65
66
|
default: "oAuth2"
|
|
66
67
|
},
|
|
@@ -120,6 +121,11 @@ var Resend = class {
|
|
|
120
121
|
value: "segments",
|
|
121
122
|
description: "Create, update, delete, or list contact segments for grouping contacts based on criteria"
|
|
122
123
|
},
|
|
124
|
+
{
|
|
125
|
+
name: "Suppression",
|
|
126
|
+
value: "suppressions",
|
|
127
|
+
description: "Add, remove, or list suppressed email addresses that Resend skips at send time to avoid hard bounces and spam complaints"
|
|
128
|
+
},
|
|
123
129
|
{
|
|
124
130
|
name: "Template",
|
|
125
131
|
value: "templates",
|
|
@@ -149,6 +155,7 @@ var Resend = class {
|
|
|
149
155
|
...require_nodes_Resend_actions_domain_index.descriptions,
|
|
150
156
|
...require_nodes_Resend_actions_broadcast_index.descriptions,
|
|
151
157
|
...require_nodes_Resend_actions_segment_index.descriptions,
|
|
158
|
+
...require_nodes_Resend_actions_suppression_index.descriptions,
|
|
152
159
|
...require_nodes_Resend_actions_topic_index.descriptions,
|
|
153
160
|
...require_nodes_Resend_actions_contact_index.descriptions,
|
|
154
161
|
...require_nodes_Resend_actions_contactProperty_index.descriptions,
|
|
@@ -168,6 +175,7 @@ var Resend = class {
|
|
|
168
175
|
getEmails: require_nodes_Resend_methods_index.getEmails,
|
|
169
176
|
getReceivedEmails: require_nodes_Resend_methods_index.getReceivedEmails,
|
|
170
177
|
getSegments: require_nodes_Resend_methods_index.getSegments,
|
|
178
|
+
getSuppressions: require_nodes_Resend_methods_index.getSuppressions,
|
|
171
179
|
getTemplateVariables: require_nodes_Resend_methods_index.getTemplateVariables,
|
|
172
180
|
getTemplates: require_nodes_Resend_methods_index.getTemplates,
|
|
173
181
|
getTopics: require_nodes_Resend_methods_index.getTopics,
|
|
@@ -181,6 +189,7 @@ var Resend = class {
|
|
|
181
189
|
getEmails: require_nodes_Resend_methods_index.getEmailsListSearch,
|
|
182
190
|
getReceivedEmails: require_nodes_Resend_methods_index.getReceivedEmailsListSearch,
|
|
183
191
|
getSegments: require_nodes_Resend_methods_index.getSegmentsListSearch,
|
|
192
|
+
getSuppressions: require_nodes_Resend_methods_index.getSuppressionsListSearch,
|
|
184
193
|
getTemplates: require_nodes_Resend_methods_index.getTemplatesListSearch,
|
|
185
194
|
getTemplateVariables: require_nodes_Resend_methods_index.getTemplateVariablesListSearch,
|
|
186
195
|
getTopics: require_nodes_Resend_methods_index.getTopicsListSearch,
|
|
@@ -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:
|
|
76
|
+
required: false,
|
|
77
|
+
testedBy: "resendWebhookSigningSecretTest"
|
|
78
|
+
}, {
|
|
79
|
+
name: "resendApi",
|
|
80
|
+
required: false
|
|
52
81
|
}],
|
|
53
82
|
triggerPanel: {
|
|
54
|
-
header: "
|
|
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
|
|
164
|
-
|
|
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
|
|
281
|
+
} catch {
|
|
177
282
|
this.getResponseObject().status(401).json({ error: "Invalid webhook signature" });
|
|
178
283
|
return { noWebhookResponse: true };
|
|
179
284
|
}
|
|
@@ -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
|
|
22
|
+
action: "Disconnect the resend account"
|
|
23
23
|
}],
|
|
24
24
|
default: "disconnect"
|
|
25
25
|
}];
|
|
@@ -20,10 +20,10 @@ const description = [require_nodes_Resend_utils_dynamicFields.createDynamicIdFie
|
|
|
20
20
|
} }
|
|
21
21
|
})];
|
|
22
22
|
async function execute(index) {
|
|
23
|
-
var
|
|
23
|
+
var _ref;
|
|
24
24
|
const domainId = require_nodes_Resend_utils_dynamicFields.resolveDynamicIdValue(this, "domainId", index);
|
|
25
25
|
const response = await require_nodes_Resend_transport_index.apiRequest.call(this, "GET", `/domains/${encodeURIComponent(domainId)}/tracking-domains`);
|
|
26
|
-
const executionData = ((
|
|
26
|
+
const executionData = ((_ref = response.data) !== null && _ref !== void 0 ? _ref : []).map((item) => ({
|
|
27
27
|
json: item,
|
|
28
28
|
pairedItem: { item: index }
|
|
29
29
|
}));
|
|
@@ -461,7 +461,7 @@ async function execute(index) {
|
|
|
461
461
|
if (additionalOptions.idempotencyKey) requestHeaders["Idempotency-Key"] = additionalOptions.idempotencyKey;
|
|
462
462
|
let response;
|
|
463
463
|
try {
|
|
464
|
-
response = await this.helpers.httpRequestWithAuthentication.call(this,
|
|
464
|
+
response = await this.helpers.httpRequestWithAuthentication.call(this, require_nodes_Resend_transport_index.getCredentialType(this), {
|
|
465
465
|
url: `${require_nodes_Resend_transport_index.RESEND_API_BASE}/emails`,
|
|
466
466
|
method: "POST",
|
|
467
467
|
headers: requestHeaders,
|
|
@@ -3,6 +3,6 @@ declare namespace sendAndWait_operation_d_exports {
|
|
|
3
3
|
export { SEND_AND_WAIT_OPERATION, description, execute };
|
|
4
4
|
}
|
|
5
5
|
declare const description: INodeProperties[];
|
|
6
|
-
declare function execute(this: IExecuteFunctions
|
|
6
|
+
declare function execute(this: IExecuteFunctions): Promise<INodeExecutionData[]>;
|
|
7
7
|
//#endregion
|
|
8
8
|
export { SEND_AND_WAIT_OPERATION, description, execute, sendAndWait_operation_d_exports };
|
|
@@ -29,7 +29,7 @@ const description = require_nodes_Resend_utils_sendAndWait_utils.getSendAndWaitP
|
|
|
29
29
|
defaultApproveLabel: "Approve",
|
|
30
30
|
defaultDisapproveLabel: "Decline"
|
|
31
31
|
});
|
|
32
|
-
async function execute(
|
|
32
|
+
async function execute() {
|
|
33
33
|
const email = require_nodes_Resend_utils_sendAndWait_utils.createEmail(this);
|
|
34
34
|
await require_nodes_Resend_utils_sendAndWait_utils.sendResendEmail(this, email);
|
|
35
35
|
const waitTill = require_nodes_Resend_utils_sendAndWait_utils.configureWaitTillDate(this);
|
|
@@ -48,7 +48,8 @@ const description = [{
|
|
|
48
48
|
displayName: "Headers",
|
|
49
49
|
name: "headers",
|
|
50
50
|
type: "fixedCollection",
|
|
51
|
-
default: {},
|
|
51
|
+
default: { headers: [] },
|
|
52
|
+
typeOptions: { multipleValues: true },
|
|
52
53
|
options: [{
|
|
53
54
|
name: "headers",
|
|
54
55
|
displayName: "Header",
|
|
@@ -74,11 +75,20 @@ const description = [{
|
|
|
74
75
|
default: "",
|
|
75
76
|
description: "Email address where replies should be sent. Can be different from the sender address."
|
|
76
77
|
},
|
|
78
|
+
{
|
|
79
|
+
displayName: "Scheduled At",
|
|
80
|
+
name: "scheduledAt",
|
|
81
|
+
type: "string",
|
|
82
|
+
default: "",
|
|
83
|
+
placeholder: "in 1 hour",
|
|
84
|
+
description: "Schedule this email to be sent at a future time. Accepts natural language like \"in 1 hour\" or ISO 8601 format \"2024-12-25T09:00:00Z\". Each email in the batch can be scheduled independently."
|
|
85
|
+
},
|
|
77
86
|
{
|
|
78
87
|
displayName: "Tags",
|
|
79
88
|
name: "tags",
|
|
80
89
|
type: "fixedCollection",
|
|
81
|
-
default: {},
|
|
90
|
+
default: { tags: [] },
|
|
91
|
+
typeOptions: { multipleValues: true },
|
|
82
92
|
options: [{
|
|
83
93
|
name: "tags",
|
|
84
94
|
displayName: "Tag",
|
|
@@ -170,7 +180,8 @@ const description = [{
|
|
|
170
180
|
displayName: "Template Variables",
|
|
171
181
|
name: "templateVariables",
|
|
172
182
|
type: "fixedCollection",
|
|
173
|
-
default: {},
|
|
183
|
+
default: { variables: [] },
|
|
184
|
+
typeOptions: { multipleValues: true },
|
|
174
185
|
description: "Key-value pairs to replace template placeholders. Keys must match the template variable names.",
|
|
175
186
|
options: [{
|
|
176
187
|
name: "variables",
|
|
@@ -298,6 +309,8 @@ async function execute(index) {
|
|
|
298
309
|
});
|
|
299
310
|
const topicId = additionalOptions.topicId;
|
|
300
311
|
if (topicId) emailObj.topic_id = topicId;
|
|
312
|
+
const scheduledAt = additionalOptions.scheduledAt;
|
|
313
|
+
if (scheduledAt) emailObj.scheduled_at = scheduledAt;
|
|
301
314
|
if (contentType === "template") {
|
|
302
315
|
if (!email.templateId) throw new n8n_workflow.NodeOperationError(this.getNode(), "Template Name or ID is required for batch emails when using templates.", { itemIndex: index });
|
|
303
316
|
if (email.html || email.text) throw new n8n_workflow.NodeOperationError(this.getNode(), "HTML/Text Content cannot be used when sending batch emails with templates.", { itemIndex: index });
|
|
@@ -322,7 +335,7 @@ async function execute(index) {
|
|
|
322
335
|
if (batchOptions.idempotency_key) headers["Idempotency-Key"] = batchOptions.idempotency_key;
|
|
323
336
|
let response;
|
|
324
337
|
try {
|
|
325
|
-
response = await this.helpers.httpRequestWithAuthentication.call(this,
|
|
338
|
+
response = await this.helpers.httpRequestWithAuthentication.call(this, require_nodes_Resend_transport_index.getCredentialType(this), {
|
|
326
339
|
url: `${require_nodes_Resend_transport_index.RESEND_API_BASE}/emails/batch`,
|
|
327
340
|
method: "POST",
|
|
328
341
|
headers,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_nodes_Resend_transport_index = require("../transport/index.js");
|
|
2
3
|
const require_nodes_Resend_actions_account_index = require("./account/index.js");
|
|
3
4
|
const require_nodes_Resend_actions_broadcast_index = require("./broadcast/index.js");
|
|
4
5
|
const require_nodes_Resend_actions_contact_index = require("./contact/index.js");
|
|
@@ -9,6 +10,7 @@ const require_nodes_Resend_actions_event_index = require("./event/index.js");
|
|
|
9
10
|
const require_nodes_Resend_actions_log_index = require("./log/index.js");
|
|
10
11
|
const require_nodes_Resend_actions_receivingEmail_index = require("./receivingEmail/index.js");
|
|
11
12
|
const require_nodes_Resend_actions_segment_index = require("./segment/index.js");
|
|
13
|
+
const require_nodes_Resend_actions_suppression_index = require("./suppression/index.js");
|
|
12
14
|
const require_nodes_Resend_actions_template_index = require("./template/index.js");
|
|
13
15
|
const require_nodes_Resend_actions_topic_index = require("./topic/index.js");
|
|
14
16
|
const require_nodes_Resend_actions_webhook_index = require("./webhook/index.js");
|
|
@@ -22,6 +24,7 @@ const resourceModules = {
|
|
|
22
24
|
domains: require_nodes_Resend_actions_domain_index.domain_exports,
|
|
23
25
|
broadcasts: require_nodes_Resend_actions_broadcast_index.broadcast_exports,
|
|
24
26
|
segments: require_nodes_Resend_actions_segment_index.segment_exports,
|
|
27
|
+
suppressions: require_nodes_Resend_actions_suppression_index.suppression_exports,
|
|
25
28
|
topics: require_nodes_Resend_actions_topic_index.topic_exports,
|
|
26
29
|
contacts: require_nodes_Resend_actions_contact_index.contact_exports,
|
|
27
30
|
contactProperties: require_nodes_Resend_actions_contactProperty_index.contactProperty_exports,
|
|
@@ -54,7 +57,7 @@ async function router() {
|
|
|
54
57
|
});
|
|
55
58
|
continue;
|
|
56
59
|
}
|
|
57
|
-
|
|
60
|
+
require_nodes_Resend_transport_index.handleResendApiError(this.getNode(), error, i);
|
|
58
61
|
}
|
|
59
62
|
return [returnData];
|
|
60
63
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData, INodeProperties } from "n8n-workflow";
|
|
2
|
+
declare namespace batchAdd_operation_d_exports {
|
|
3
|
+
export { description, execute };
|
|
4
|
+
}
|
|
5
|
+
declare const description: INodeProperties[];
|
|
6
|
+
declare function execute(this: IExecuteFunctions, index: number): Promise<INodeExecutionData[]>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { batchAdd_operation_d_exports, description, execute };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_runtime = require("../../../../_virtual/_rolldown/runtime.js");
|
|
3
|
+
const require_nodes_Resend_transport_index = require("../../transport/index.js");
|
|
4
|
+
let n8n_workflow = require("n8n-workflow");
|
|
5
|
+
//#region nodes/Resend/actions/suppression/batchAdd.operation.ts
|
|
6
|
+
var batchAdd_operation_exports = /* @__PURE__ */ require_runtime.__exportAll({
|
|
7
|
+
description: () => description,
|
|
8
|
+
execute: () => execute
|
|
9
|
+
});
|
|
10
|
+
const description = [{
|
|
11
|
+
displayName: "Emails",
|
|
12
|
+
name: "suppressionEmails",
|
|
13
|
+
type: "string",
|
|
14
|
+
required: true,
|
|
15
|
+
default: "",
|
|
16
|
+
placeholder: "one@example.com, two@example.com",
|
|
17
|
+
displayOptions: { show: {
|
|
18
|
+
resource: ["suppressions"],
|
|
19
|
+
operation: ["batchAdd"]
|
|
20
|
+
} },
|
|
21
|
+
description: "Comma-separated list of email addresses to add to the suppression list. Up to 100 addresses per request."
|
|
22
|
+
}];
|
|
23
|
+
async function execute(index) {
|
|
24
|
+
const emails = require_nodes_Resend_transport_index.normalizeEmailList(this.getNodeParameter("suppressionEmails", index));
|
|
25
|
+
if (emails.length === 0) throw new n8n_workflow.NodeOperationError(this.getNode(), "Provide at least one email address to suppress", { itemIndex: index });
|
|
26
|
+
if (emails.length > 100) throw new n8n_workflow.NodeOperationError(this.getNode(), "A batch add supports at most 100 email addresses per request", { itemIndex: index });
|
|
27
|
+
const body = { emails };
|
|
28
|
+
return [{
|
|
29
|
+
json: await require_nodes_Resend_transport_index.apiRequest.call(this, "POST", "/suppressions/batch/add", body),
|
|
30
|
+
pairedItem: { item: index }
|
|
31
|
+
}];
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
Object.defineProperty(exports, "batchAdd_operation_exports", {
|
|
35
|
+
enumerable: true,
|
|
36
|
+
get: function() {
|
|
37
|
+
return batchAdd_operation_exports;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
exports.description = description;
|
|
41
|
+
exports.execute = execute;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData, INodeProperties } from "n8n-workflow";
|
|
2
|
+
declare namespace batchRemove_operation_d_exports {
|
|
3
|
+
export { description, execute };
|
|
4
|
+
}
|
|
5
|
+
declare const description: INodeProperties[];
|
|
6
|
+
declare function execute(this: IExecuteFunctions, index: number): Promise<INodeExecutionData[]>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { batchRemove_operation_d_exports, description, execute };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_runtime = require("../../../../_virtual/_rolldown/runtime.js");
|
|
3
|
+
const require_nodes_Resend_transport_index = require("../../transport/index.js");
|
|
4
|
+
let n8n_workflow = require("n8n-workflow");
|
|
5
|
+
//#region nodes/Resend/actions/suppression/batchRemove.operation.ts
|
|
6
|
+
var batchRemove_operation_exports = /* @__PURE__ */ require_runtime.__exportAll({
|
|
7
|
+
description: () => description,
|
|
8
|
+
execute: () => execute
|
|
9
|
+
});
|
|
10
|
+
const description = [
|
|
11
|
+
{
|
|
12
|
+
displayName: "Remove By",
|
|
13
|
+
name: "suppressionRemoveBy",
|
|
14
|
+
type: "options",
|
|
15
|
+
default: "emails",
|
|
16
|
+
displayOptions: { show: {
|
|
17
|
+
resource: ["suppressions"],
|
|
18
|
+
operation: ["batchRemove"]
|
|
19
|
+
} },
|
|
20
|
+
options: [{
|
|
21
|
+
name: "Emails",
|
|
22
|
+
value: "emails",
|
|
23
|
+
description: "Remove suppressions matching these email addresses"
|
|
24
|
+
}, {
|
|
25
|
+
name: "IDs",
|
|
26
|
+
value: "ids",
|
|
27
|
+
description: "Remove suppressions matching these suppression IDs"
|
|
28
|
+
}],
|
|
29
|
+
description: "Whether to remove suppressions by email address or by suppression ID. Provide one or the other, not both."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
displayName: "Emails",
|
|
33
|
+
name: "suppressionEmails",
|
|
34
|
+
type: "string",
|
|
35
|
+
required: true,
|
|
36
|
+
default: "",
|
|
37
|
+
placeholder: "one@example.com, two@example.com",
|
|
38
|
+
displayOptions: { show: {
|
|
39
|
+
resource: ["suppressions"],
|
|
40
|
+
operation: ["batchRemove"],
|
|
41
|
+
suppressionRemoveBy: ["emails"]
|
|
42
|
+
} },
|
|
43
|
+
description: "Comma-separated list of email addresses to remove from the suppression list. Up to 100 addresses per request."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
displayName: "IDs",
|
|
47
|
+
name: "suppressionIds",
|
|
48
|
+
type: "string",
|
|
49
|
+
required: true,
|
|
50
|
+
default: "",
|
|
51
|
+
placeholder: "e169aa45-1ecf-4183-9955-b1499d5701d3, ...",
|
|
52
|
+
displayOptions: { show: {
|
|
53
|
+
resource: ["suppressions"],
|
|
54
|
+
operation: ["batchRemove"],
|
|
55
|
+
suppressionRemoveBy: ["ids"]
|
|
56
|
+
} },
|
|
57
|
+
description: "Comma-separated list of suppression IDs to remove from the suppression list. Up to 100 IDs per request."
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
async function execute(index) {
|
|
61
|
+
const removeBy = this.getNodeParameter("suppressionRemoveBy", index, "emails");
|
|
62
|
+
const field = removeBy === "ids" ? "suppressionIds" : "suppressionEmails";
|
|
63
|
+
const values = require_nodes_Resend_transport_index.normalizeEmailList(this.getNodeParameter(field, index));
|
|
64
|
+
if (values.length === 0) throw new n8n_workflow.NodeOperationError(this.getNode(), `Provide at least one ${removeBy === "ids" ? "suppression ID" : "email address"} to remove`, { itemIndex: index });
|
|
65
|
+
if (values.length > 100) throw new n8n_workflow.NodeOperationError(this.getNode(), "A batch remove supports at most 100 entries per request", { itemIndex: index });
|
|
66
|
+
const body = { [removeBy]: values };
|
|
67
|
+
return [{
|
|
68
|
+
json: await require_nodes_Resend_transport_index.apiRequest.call(this, "POST", "/suppressions/batch/remove", body),
|
|
69
|
+
pairedItem: { item: index }
|
|
70
|
+
}];
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
Object.defineProperty(exports, "batchRemove_operation_exports", {
|
|
74
|
+
enumerable: true,
|
|
75
|
+
get: function() {
|
|
76
|
+
return batchRemove_operation_exports;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
exports.description = description;
|
|
80
|
+
exports.execute = execute;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData, INodeProperties } from "n8n-workflow";
|
|
2
|
+
declare namespace create_operation_d_exports {
|
|
3
|
+
export { description, execute };
|
|
4
|
+
}
|
|
5
|
+
declare const description: INodeProperties[];
|
|
6
|
+
declare function execute(this: IExecuteFunctions, index: number): Promise<INodeExecutionData[]>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { create_operation_d_exports, description, execute };
|