diff --git a/credentials/AutosendApi.credentials.ts b/credentials/AutosendApi.credentials.ts
new file mode 100644
index 0000000..2867832
--- /dev/null
+++ b/credentials/AutosendApi.credentials.ts
@@ -0,0 +1,53 @@
+import type {
+ IAuthenticateGeneric,
+ ICredentialTestRequest,
+ ICredentialType,
+ INodeProperties,
+} from 'n8n-workflow';
+
+export class AutosendApi implements ICredentialType {
+ name = 'autosendApi';
+
+ displayName = 'Autosend API';
+
+ documentationUrl = 'https://docs.autosend.com/';
+
+ icon = 'file:../icons/autosend.svg' as const;
+
+ httpRequestNode = {
+ name: 'Autosend',
+ docsUrl: 'https://docs.autosend.com/',
+ apiBaseUrl: 'https://api.autosend.com/',
+ };
+
+ properties: INodeProperties[] = [
+ {
+ displayName: 'API Key',
+ name: 'apiKey',
+ type: 'string',
+ typeOptions: {
+ password: true,
+ },
+ default: '',
+ required: true,
+ description: 'API key from your Autosend account settings',
+ },
+ ];
+
+ authenticate: IAuthenticateGeneric = {
+ type: 'generic',
+ properties: {
+ headers: {
+ Authorization: '=Bearer {{$credentials.apiKey}}',
+ },
+ },
+ };
+
+ test: ICredentialTestRequest = {
+ request: {
+ baseURL: 'https://api.autosend.com',
+ url: '/contacts',
+ method: 'GET',
+ },
+ };
+}
diff --git a/icons/autosend.dark.svg b/icons/autosend.dark.svg
new file mode 100644
index 0000000..b856ab9
--- /dev/null
+++ b/icons/autosend.dark.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/autosend.svg b/icons/autosend.svg
new file mode 100644
index 0000000..f1001ce
--- /dev/null
+++ b/icons/autosend.svg
@@ -0,0 +1,6 @@
+
diff --git a/nodes/Autosend/Autosend.node.json b/nodes/Autosend/Autosend.node.json
new file mode 100644
index 0000000..e4ee493
--- /dev/null
+++ b/nodes/Autosend/Autosend.node.json
@@ -0,0 +1,18 @@
+{
+ "node": "n8n-nodes-base.autosend",
+ "nodeVersion": "1.0",
+ "codexVersion": "1.0",
+ "categories": ["Communication"],
+ "resources": {
+ "credentialDocumentation": [
+ {
+ "url": "https://docs.autosend.com/"
+ }
+ ],
+ "primaryDocumentation": [
+ {
+ "url": "https://docs.autosend.com/"
+ }
+ ]
+ }
+}
diff --git a/nodes/Autosend/Autosend.node.ts b/nodes/Autosend/Autosend.node.ts
new file mode 100644
index 0000000..5ce2273
--- /dev/null
+++ b/nodes/Autosend/Autosend.node.ts
@@ -0,0 +1,71 @@
+import type {
+ INodeType,
+ INodeTypeDescription,
+ INodeExecutionData,
+ IExecuteFunctions,
+} from 'n8n-workflow';
+import { mailFields, mailOperations } from './resources/mail';
+import { contactFields, contactOperations } from './resources/contact';
+
+export class Autosend implements INodeType {
+ description: INodeTypeDescription = {
+ displayName: 'Autosend',
+ name: 'autosend',
+ icon: 'file:autosend.svg',
+ group: ['transform'],
+ version: 1,
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
+ description: 'Interact with Autosend API to send emails and manage contacts',
+ defaults: {
+ name: 'Autosend',
+ },
+ inputs: ['main'],
+ outputs: ['main'],
+ credentials: [
+ {
+ name: 'autosendApi',
+ required: true,
+ },
+ ],
+ requestDefaults: {
+ baseURL: 'https://api.autosend.com',
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ },
+ },
+ properties: [
+ {
+ displayName: 'Resource',
+ name: 'resource',
+ type: 'options',
+ noDataExpression: true,
+ options: [
+ {
+ name: 'Mail',
+ value: 'mail',
+ description: 'Send emails using Autosend',
+ },
+ {
+ name: 'Contact',
+ value: 'contact',
+ description: 'Manage contacts in Autosend',
+ },
+ ],
+ default: 'mail',
+ },
+ ...mailOperations,
+ ...mailFields,
+ ...contactOperations,
+ ...contactFields,
+ ],
+ usableAsTool: true,
+ };
+
+ async execute(this: IExecuteFunctions): Promise {
+ // This method is required but the declarative routing handles everything
+ // This is only called if routing doesn't handle the request
+ const items = this.getInputData();
+ return [items];
+ }
+}
diff --git a/nodes/Autosend/autosend.dark.svg b/nodes/Autosend/autosend.dark.svg
new file mode 100644
index 0000000..b856ab9
--- /dev/null
+++ b/nodes/Autosend/autosend.dark.svg
@@ -0,0 +1,6 @@
+
diff --git a/nodes/Autosend/autosend.svg b/nodes/Autosend/autosend.svg
new file mode 100644
index 0000000..f1001ce
--- /dev/null
+++ b/nodes/Autosend/autosend.svg
@@ -0,0 +1,6 @@
+
diff --git a/nodes/Autosend/resources/contact/get.ts b/nodes/Autosend/resources/contact/get.ts
new file mode 100644
index 0000000..6eefd5c
--- /dev/null
+++ b/nodes/Autosend/resources/contact/get.ts
@@ -0,0 +1,80 @@
+import type { INodeProperties } from 'n8n-workflow';
+
+export const getOperation: INodeProperties[] = [
+ {
+ displayName: 'Search By',
+ name: 'searchBy',
+ type: 'options',
+ required: true,
+ default: 'email',
+ options: [
+ {
+ name: 'Email',
+ value: 'email',
+ description: 'Search contact by email address',
+ },
+ {
+ name: 'ID',
+ value: 'id',
+ description: 'Search contact by ID',
+ },
+ ],
+ displayOptions: {
+ show: {
+ resource: ['contact'],
+ operation: ['get'],
+ },
+ },
+ description: 'How to search for the contact',
+ },
+ {
+ displayName: 'Contact ID',
+ name: 'contactId',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['contact'],
+ operation: ['get'],
+ searchBy: ['id'],
+ },
+ },
+ routing: {
+ request: {
+ url: '=/contacts/{{$parameter.contactId}}',
+ },
+ },
+ description: 'The ID of the contact to retrieve',
+ },
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ required: true,
+ placeholder: 'contact@example.com',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['contact'],
+ operation: ['get'],
+ searchBy: ['email'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'emails',
+ preSend: [
+ async function (this, requestOptions) {
+ const email = this.getNodeParameter('email') as string;
+ requestOptions.body = requestOptions.body || {};
+ (requestOptions.body as Record).emails = [email];
+ return requestOptions;
+ },
+ ],
+ },
+ },
+ description: 'The email address of the contact to search for',
+ },
+];
diff --git a/nodes/Autosend/resources/contact/index.ts b/nodes/Autosend/resources/contact/index.ts
new file mode 100644
index 0000000..d906d31
--- /dev/null
+++ b/nodes/Autosend/resources/contact/index.ts
@@ -0,0 +1,64 @@
+import type { INodeProperties } from 'n8n-workflow';
+import { upsertOperation } from './upsert';
+import { getOperation } from './get';
+
+export const contactOperations: INodeProperties[] = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ noDataExpression: true,
+ displayOptions: {
+ show: {
+ resource: ['contact'],
+ },
+ },
+ options: [
+ {
+ name: 'Create or Update',
+ value: 'upsert',
+ description: 'Create a new record, or update the current one if it already exists (upsert)',
+ action: 'Create or update a contact',
+ routing: {
+ request: {
+ method: 'POST' as const,
+ url: '/contacts/email',
+ },
+ },
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Get a contact by ID or email',
+ action: 'Get a contact',
+ routing: {
+ request: {
+ method: 'GET' as const,
+ url: '/contacts/{{$parameter.contactId}}',
+ },
+ },
+ },
+ ],
+ default: 'upsert',
+ },
+];
+
+export const contactFields: INodeProperties[] = [
+ ...upsertOperation,
+ ...getOperation.map((field) => {
+ // Override routing for email search to use the search endpoint
+ if (field.name === 'email' && field.routing) {
+ return {
+ ...field,
+ routing: {
+ ...field.routing,
+ request: {
+ method: 'POST' as const,
+ url: '/contacts/search/emails',
+ },
+ },
+ } as INodeProperties;
+ }
+ return field;
+ }),
+];
diff --git a/nodes/Autosend/resources/contact/upsert.ts b/nodes/Autosend/resources/contact/upsert.ts
new file mode 100644
index 0000000..95e7652
--- /dev/null
+++ b/nodes/Autosend/resources/contact/upsert.ts
@@ -0,0 +1,142 @@
+import type { INodeProperties } from 'n8n-workflow';
+
+export const upsertOperation: INodeProperties[] = [
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ required: true,
+ placeholder: 'contact@example.com',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['contact'],
+ operation: ['upsert'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'email',
+ },
+ },
+ description: 'The email address of the contact',
+ },
+ {
+ displayName: 'User ID',
+ name: 'userId',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['contact'],
+ operation: ['upsert'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'userId',
+ },
+ },
+ description: 'External user ID from your system',
+ },
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: ['contact'],
+ operation: ['upsert'],
+ },
+ },
+ options: [
+ {
+ displayName: 'Company',
+ name: 'company',
+ type: 'string',
+ default: '',
+ routing: {
+ send: {
+ type: 'body',
+ property: 'company',
+ },
+ },
+ description: 'Company name',
+ },
+ {
+ displayName: 'Custom Fields (JSON)',
+ name: 'customFields',
+ type: 'json',
+ default: '{}',
+ routing: {
+ send: {
+ type: 'body',
+ property: 'customFields',
+ preSend: [
+ async function (this, requestOptions) {
+ const customFieldsStr = this.getNodeParameter(
+ 'additionalFields.customFields',
+ '',
+ ) as string;
+ if (customFieldsStr) {
+ try {
+ const customFields = JSON.parse(customFieldsStr);
+ requestOptions.body = requestOptions.body || {};
+ (requestOptions.body as Record).customFields = customFields;
+ } catch {
+ throw new Error('Custom Fields must be valid JSON');
+ }
+ }
+ return requestOptions;
+ },
+ ],
+ },
+ },
+ description: 'Additional custom fields as a JSON object',
+ },
+ {
+ displayName: 'First Name',
+ name: 'firstName',
+ type: 'string',
+ default: '',
+ routing: {
+ send: {
+ type: 'body',
+ property: 'firstName',
+ },
+ },
+ description: 'First name of the contact',
+ },
+ {
+ displayName: 'Last Name',
+ name: 'lastName',
+ type: 'string',
+ default: '',
+ routing: {
+ send: {
+ type: 'body',
+ property: 'lastName',
+ },
+ },
+ description: 'Last name of the contact',
+ },
+ {
+ displayName: 'Phone',
+ name: 'phone',
+ type: 'string',
+ default: '',
+ routing: {
+ send: {
+ type: 'body',
+ property: 'phone',
+ },
+ },
+ description: 'Phone number of the contact',
+ },
+ ],
+ },
+];
diff --git a/nodes/Autosend/resources/mail/index.ts b/nodes/Autosend/resources/mail/index.ts
new file mode 100644
index 0000000..d99e5e7
--- /dev/null
+++ b/nodes/Autosend/resources/mail/index.ts
@@ -0,0 +1,46 @@
+import type { INodeProperties } from 'n8n-workflow';
+import { sendOperation } from './send';
+import { sendBulkOperation } from './sendBulk';
+
+export const mailOperations: INodeProperties[] = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ noDataExpression: true,
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ },
+ },
+ options: [
+ {
+ name: 'Send',
+ value: 'send',
+ description: 'Send a single email',
+ action: 'Send an email',
+ routing: {
+ request: {
+ method: 'POST',
+ url: '/mails/send',
+ },
+ },
+ },
+ {
+ name: 'Send Bulk',
+ value: 'sendBulk',
+ description: 'Send emails to multiple recipients',
+ action: 'Send bulk emails',
+ routing: {
+ request: {
+ method: 'POST',
+ url: '/mails/bulk',
+ },
+ },
+ },
+ ],
+ default: 'send',
+ },
+];
+
+export const mailFields: INodeProperties[] = [...sendOperation, ...sendBulkOperation];
diff --git a/nodes/Autosend/resources/mail/send.ts b/nodes/Autosend/resources/mail/send.ts
new file mode 100644
index 0000000..43377f2
--- /dev/null
+++ b/nodes/Autosend/resources/mail/send.ts
@@ -0,0 +1,322 @@
+import type { INodeProperties } from 'n8n-workflow';
+
+export const sendOperation: INodeProperties[] = [
+ {
+ displayName: 'From Email',
+ name: 'fromEmail',
+ type: 'string',
+ required: true,
+ placeholder: 'noreply@example.com',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['send'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'from.email',
+ },
+ },
+ description: 'The email address to send from',
+ },
+ {
+ displayName: 'From Name',
+ name: 'fromName',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['send'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'from.name',
+ },
+ },
+ description: 'The name to display as the sender',
+ },
+ {
+ displayName: 'To Email',
+ name: 'toEmail',
+ type: 'string',
+ required: true,
+ placeholder: 'recipient@example.com',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['send'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'to.email',
+ },
+ },
+ description: 'The email address to send to',
+ },
+ {
+ displayName: 'To Name',
+ name: 'toName',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['send'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'to.name',
+ },
+ },
+ description: 'The name of the recipient',
+ },
+ {
+ displayName: 'Email Content Type',
+ name: 'contentType',
+ type: 'options',
+ required: true,
+ default: 'template',
+ options: [
+ {
+ name: 'Template',
+ value: 'template',
+ description: 'Use a pre-defined email template',
+ },
+ {
+ name: 'Custom',
+ value: 'custom',
+ description: 'Provide custom HTML and text content',
+ },
+ ],
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['send'],
+ },
+ },
+ description: 'Whether to use a template or custom content',
+ },
+ // Template-based fields
+ {
+ displayName: 'Template ID',
+ name: 'templateId',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['send'],
+ contentType: ['template'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'templateId',
+ },
+ },
+ description: 'The ID of the email template to use',
+ },
+ {
+ displayName: 'Template Variables',
+ name: 'templateVariables',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: true,
+ },
+ default: {},
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['send'],
+ contentType: ['template'],
+ },
+ },
+ options: [
+ {
+ name: 'variables',
+ displayName: 'Variable',
+ values: [
+ {
+ displayName: 'Key',
+ name: 'key',
+ type: 'string',
+ default: '',
+ description: 'Variable name',
+ },
+ {
+ displayName: 'Value',
+ name: 'value',
+ type: 'string',
+ default: '',
+ description: 'Variable value',
+ },
+ ],
+ },
+ ],
+ routing: {
+ send: {
+ type: 'body',
+ property: 'variables',
+ preSend: [
+ // Transform the fixed collection into a simple object
+ async function (this, requestOptions) {
+ const variables = this.getNodeParameter('templateVariables') as {
+ variables?: Array<{ key: string; value: string }>;
+ };
+ if (variables?.variables && Array.isArray(variables.variables)) {
+ const variablesObj: Record = {};
+ for (const variable of variables.variables) {
+ if (variable.key) {
+ variablesObj[variable.key] = variable.value;
+ }
+ }
+ requestOptions.body = requestOptions.body || {};
+ (requestOptions.body as Record).variables = variablesObj;
+ }
+ return requestOptions;
+ },
+ ],
+ },
+ },
+ description: 'Variables to use in the email template',
+ },
+ // Custom content fields
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['send'],
+ contentType: ['custom'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'subject',
+ },
+ },
+ description: 'The email subject line',
+ },
+ {
+ displayName: 'HTML Content',
+ name: 'html',
+ type: 'string',
+ typeOptions: {
+ rows: 5,
+ },
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['send'],
+ contentType: ['custom'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'html',
+ },
+ },
+ description: 'The HTML content of the email',
+ },
+ {
+ displayName: 'Text Content',
+ name: 'text',
+ type: 'string',
+ typeOptions: {
+ rows: 5,
+ },
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['send'],
+ contentType: ['custom'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'text',
+ },
+ },
+ description: 'The plain text content of the email (fallback for non-HTML clients)',
+ },
+ // Additional options
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['send'],
+ },
+ },
+ options: [
+ {
+ displayName: 'Reply To',
+ name: 'replyTo',
+ type: 'string',
+ default: '',
+ placeholder: 'reply@example.com',
+ routing: {
+ send: {
+ type: 'body',
+ property: 'replyTo',
+ },
+ },
+ description: 'Email address for replies',
+ },
+ {
+ displayName: 'CC',
+ name: 'cc',
+ type: 'string',
+ default: '',
+ placeholder: 'cc@example.com',
+ routing: {
+ send: {
+ type: 'body',
+ property: 'cc',
+ },
+ },
+ description: 'Carbon copy recipients (comma-separated)',
+ },
+ {
+ displayName: 'BCC',
+ name: 'bcc',
+ type: 'string',
+ default: '',
+ placeholder: 'bcc@example.com',
+ routing: {
+ send: {
+ type: 'body',
+ property: 'bcc',
+ },
+ },
+ description: 'Blind carbon copy recipients (comma-separated)',
+ },
+ ],
+ },
+];
diff --git a/nodes/Autosend/resources/mail/sendBulk.ts b/nodes/Autosend/resources/mail/sendBulk.ts
new file mode 100644
index 0000000..3d5c4cf
--- /dev/null
+++ b/nodes/Autosend/resources/mail/sendBulk.ts
@@ -0,0 +1,313 @@
+import type { INodeProperties } from 'n8n-workflow';
+
+export const sendBulkOperation: INodeProperties[] = [
+ {
+ displayName: 'From Email',
+ name: 'fromEmail',
+ type: 'string',
+ required: true,
+ placeholder: 'noreply@example.com',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['sendBulk'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'from.email',
+ },
+ },
+ description: 'The email address to send from',
+ },
+ {
+ displayName: 'From Name',
+ name: 'fromName',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['sendBulk'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'from.name',
+ },
+ },
+ description: 'The name to display as the sender',
+ },
+ {
+ displayName: 'Recipients',
+ name: 'recipients',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: true,
+ multipleValueButtonText: 'Add Recipient',
+ },
+ required: true,
+ default: {},
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['sendBulk'],
+ },
+ },
+ options: [
+ {
+ name: 'recipientValues',
+ displayName: 'Recipient',
+ values: [
+ {
+ displayName: 'Email',
+ name: 'email',
+ type: 'string',
+ required: true,
+ default: '',
+ placeholder: 'recipient@example.com',
+ description: 'Recipient email address',
+ },
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ description: 'Recipient name',
+ },
+ ],
+ },
+ ],
+ routing: {
+ send: {
+ type: 'body',
+ property: 'to',
+ preSend: [
+ async function (this, requestOptions) {
+ const recipients = this.getNodeParameter('recipients') as {
+ recipientValues?: Array<{ email: string; name?: string }>;
+ };
+ if (recipients?.recipientValues && Array.isArray(recipients.recipientValues)) {
+ requestOptions.body = requestOptions.body || {};
+ (requestOptions.body as Record).to = recipients.recipientValues;
+ }
+ return requestOptions;
+ },
+ ],
+ },
+ },
+ description: 'List of recipients (up to 100 per request)',
+ },
+ {
+ displayName: 'Email Content Type',
+ name: 'contentType',
+ type: 'options',
+ required: true,
+ default: 'template',
+ options: [
+ {
+ name: 'Template',
+ value: 'template',
+ description: 'Use a pre-defined email template',
+ },
+ {
+ name: 'Custom',
+ value: 'custom',
+ description: 'Provide custom HTML and text content',
+ },
+ ],
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['sendBulk'],
+ },
+ },
+ description: 'Whether to use a template or custom content',
+ },
+ // Template-based fields
+ {
+ displayName: 'Template ID',
+ name: 'templateId',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['sendBulk'],
+ contentType: ['template'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'templateId',
+ },
+ },
+ description: 'The ID of the email template to use',
+ },
+ {
+ displayName: 'Template Variables',
+ name: 'templateVariables',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: true,
+ },
+ default: {},
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['sendBulk'],
+ contentType: ['template'],
+ },
+ },
+ options: [
+ {
+ name: 'variables',
+ displayName: 'Variable',
+ values: [
+ {
+ displayName: 'Key',
+ name: 'key',
+ type: 'string',
+ default: '',
+ description: 'Variable name',
+ },
+ {
+ displayName: 'Value',
+ name: 'value',
+ type: 'string',
+ default: '',
+ description: 'Variable value',
+ },
+ ],
+ },
+ ],
+ routing: {
+ send: {
+ type: 'body',
+ property: 'variables',
+ preSend: [
+ async function (this, requestOptions) {
+ const variables = this.getNodeParameter('templateVariables') as {
+ variables?: Array<{ key: string; value: string }>;
+ };
+ if (variables?.variables && Array.isArray(variables.variables)) {
+ const variablesObj: Record = {};
+ for (const variable of variables.variables) {
+ if (variable.key) {
+ variablesObj[variable.key] = variable.value;
+ }
+ }
+ requestOptions.body = requestOptions.body || {};
+ (requestOptions.body as Record).variables = variablesObj;
+ }
+ return requestOptions;
+ },
+ ],
+ },
+ },
+ description: 'Variables to use in the email template',
+ },
+ // Custom content fields
+ {
+ displayName: 'Subject',
+ name: 'subject',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['sendBulk'],
+ contentType: ['custom'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'subject',
+ },
+ },
+ description: 'The email subject line',
+ },
+ {
+ displayName: 'HTML Content',
+ name: 'html',
+ type: 'string',
+ typeOptions: {
+ rows: 5,
+ },
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['sendBulk'],
+ contentType: ['custom'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'html',
+ },
+ },
+ description: 'The HTML content of the email',
+ },
+ {
+ displayName: 'Text Content',
+ name: 'text',
+ type: 'string',
+ typeOptions: {
+ rows: 5,
+ },
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['sendBulk'],
+ contentType: ['custom'],
+ },
+ },
+ routing: {
+ send: {
+ type: 'body',
+ property: 'text',
+ },
+ },
+ description: 'The plain text content of the email (fallback for non-HTML clients)',
+ },
+ // Additional options
+ {
+ displayName: 'Additional Fields',
+ name: 'additionalFields',
+ type: 'collection',
+ placeholder: 'Add Field',
+ default: {},
+ displayOptions: {
+ show: {
+ resource: ['mail'],
+ operation: ['sendBulk'],
+ },
+ },
+ options: [
+ {
+ displayName: 'Reply To',
+ name: 'replyTo',
+ type: 'string',
+ default: '',
+ placeholder: 'reply@example.com',
+ routing: {
+ send: {
+ type: 'body',
+ property: 'replyTo',
+ },
+ },
+ description: 'Email address for replies',
+ },
+ ],
+ },
+];
diff --git a/package-lock.json b/package-lock.json
index 566f960..764d377 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "n8n-nodes-<...>",
+ "name": "n8n-nodes-autosend",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "n8n-nodes-<...>",
+ "name": "n8n-nodes-autosend",
"version": "0.1.0",
"license": "MIT",
"devDependencies": {
diff --git a/package.json b/package.json
index 66b8c45..acb6360 100644
--- a/package.json
+++ b/package.json
@@ -1,19 +1,19 @@
{
- "name": "n8n-nodes-<...>",
+ "name": "n8n-nodes-autosend",
"version": "0.1.0",
- "description": "",
+ "description": "n8n node for Autosend - Send emails and manage contacts",
"license": "MIT",
"homepage": "",
"keywords": [
"n8n-community-node-package"
],
"author": {
- "name": "",
- "email": ""
+ "name": "Your Name",
+ "email": "your.email@example.com"
},
"repository": {
"type": "git",
- "url": "https://github.com/<...>/n8n-nodes-<...>.git"
+ "url": "https://github.com/codebuster22/autosend-n8n-nodes.git"
},
"scripts": {
"build": "n8n-node build",
@@ -31,10 +31,12 @@
"n8nNodesApiVersion": 1,
"strict": true,
"credentials": [
+ "dist/credentials/AutosendApi.credentials.js",
"dist/credentials/GithubIssuesApi.credentials.js",
"dist/credentials/GithubIssuesOAuth2Api.credentials.js"
],
"nodes": [
+ "dist/nodes/Autosend/Autosend.node.js",
"dist/nodes/GithubIssues/GithubIssues.node.js",
"dist/nodes/Example/Example.node.js"
]