From 2f1ffde4a5383a70deb99f397856dd1c642ce1dc Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 11 Nov 2025 17:56:01 +0000 Subject: [PATCH] Add Autosend node with Mail and Contact resources Implement n8n node for Autosend API with support for: - Mail resource: Send single and bulk emails with template or custom content - Contact resource: Create/update contacts (upsert) and get contacts by ID or email - API key authentication - Declarative routing following n8n best practices - Full TypeScript support with proper typing - Passing all linting checks --- credentials/AutosendApi.credentials.ts | 53 ++++ icons/autosend.dark.svg | 6 + icons/autosend.svg | 6 + nodes/Autosend/Autosend.node.json | 18 ++ nodes/Autosend/Autosend.node.ts | 71 +++++ nodes/Autosend/autosend.dark.svg | 6 + nodes/Autosend/autosend.svg | 6 + nodes/Autosend/resources/contact/get.ts | 80 +++++ nodes/Autosend/resources/contact/index.ts | 64 ++++ nodes/Autosend/resources/contact/upsert.ts | 142 +++++++++ nodes/Autosend/resources/mail/index.ts | 46 +++ nodes/Autosend/resources/mail/send.ts | 322 +++++++++++++++++++++ nodes/Autosend/resources/mail/sendBulk.ts | 313 ++++++++++++++++++++ package-lock.json | 4 +- package.json | 12 +- 15 files changed, 1142 insertions(+), 7 deletions(-) create mode 100644 credentials/AutosendApi.credentials.ts create mode 100644 icons/autosend.dark.svg create mode 100644 icons/autosend.svg create mode 100644 nodes/Autosend/Autosend.node.json create mode 100644 nodes/Autosend/Autosend.node.ts create mode 100644 nodes/Autosend/autosend.dark.svg create mode 100644 nodes/Autosend/autosend.svg create mode 100644 nodes/Autosend/resources/contact/get.ts create mode 100644 nodes/Autosend/resources/contact/index.ts create mode 100644 nodes/Autosend/resources/contact/upsert.ts create mode 100644 nodes/Autosend/resources/mail/index.ts create mode 100644 nodes/Autosend/resources/mail/send.ts create mode 100644 nodes/Autosend/resources/mail/sendBulk.ts 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" ]