mirror of
				https://github.com/n8n-io/n8n-nodes-starter.git
				synced 2025-11-03 15:42:26 -06:00 
			
		
		
		
	feat: Add Resend node and trigger
Adds a new integration for Resend, an email sending service. Includes: - Resend API credential (`ResendApi.credentials.ts`) for API key authentication. - Resend action node (`nodes/Resend/Resend.node.ts`): - Supports "Send Email" operation with parameters for to, from, subject, html, text, cc, bcc, reply_to, and tags. - Uses Resend API `https://api.resend.com/emails`. - Resend trigger node (`nodes/Resend/ResendTrigger.node.ts`): - Handles Resend webhooks for various event types (email.sent, email.delivered, etc.). - Implements webhook signature verification using Svix and a user-provided signing secret. - Allows you to select which events to listen for. - Official Resend SVG icon (`nodes/Resend/Resend.svg`). - Adds `svix` as a dependency for webhook verification.
This commit is contained in:
		
					parent
					
						
							
								65e06338e3
							
						
					
				
			
			
				commit
				
					
						30fca2b5d5
					
				
			
		
					 6 changed files with 5148 additions and 0 deletions
				
			
		
							
								
								
									
										267
									
								
								nodes/Resend/Resend.node.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								nodes/Resend/Resend.node.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,267 @@
 | 
			
		|||
import {
 | 
			
		||||
	IExecuteFunctions,
 | 
			
		||||
	INodeExecutionData,
 | 
			
		||||
	INodeType,
 | 
			
		||||
	INodeTypeDescription,
 | 
			
		||||
	NodeOperationError,
 | 
			
		||||
} from 'n8n-workflow';
 | 
			
		||||
 | 
			
		||||
export class Resend implements INodeType {
 | 
			
		||||
	description: INodeTypeDescription = {
 | 
			
		||||
		displayName: 'Resend',
 | 
			
		||||
		name: 'resend',
 | 
			
		||||
		icon: 'file:Resend.svg',
 | 
			
		||||
		group: ['output'],
 | 
			
		||||
		version: 1,
 | 
			
		||||
		description: 'Sends emails via Resend API',
 | 
			
		||||
		defaults: {
 | 
			
		||||
			name: 'Resend',
 | 
			
		||||
			color: '#000000',
 | 
			
		||||
		},
 | 
			
		||||
		credentials: [
 | 
			
		||||
			{
 | 
			
		||||
				name: 'resendApi',
 | 
			
		||||
				required: true,
 | 
			
		||||
			},
 | 
			
		||||
		],
 | 
			
		||||
		inputs: ['main'],
 | 
			
		||||
		outputs: ['main'],
 | 
			
		||||
		properties: [
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'Operation',
 | 
			
		||||
				name: 'operation',
 | 
			
		||||
				type: 'options',
 | 
			
		||||
				noDataExpression: true,
 | 
			
		||||
				options: [
 | 
			
		||||
					{
 | 
			
		||||
						name: 'Send Email',
 | 
			
		||||
						value: 'sendEmail',
 | 
			
		||||
						action: 'Send an email',
 | 
			
		||||
						routing: {
 | 
			
		||||
							request: {
 | 
			
		||||
								method: 'POST',
 | 
			
		||||
								url: 'https://api.resend.com/emails',
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				],
 | 
			
		||||
				default: 'sendEmail',
 | 
			
		||||
			},
 | 
			
		||||
			// Properties for "Send Email" operation
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'From',
 | 
			
		||||
				name: 'fromEmail',
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				required: true,
 | 
			
		||||
				default: '',
 | 
			
		||||
				placeholder: 'you@example.com',
 | 
			
		||||
				displayOptions: {
 | 
			
		||||
					show: {
 | 
			
		||||
						operation: ['sendEmail'],
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				description: 'Sender email address',
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'To',
 | 
			
		||||
				name: 'toEmail',
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				required: true,
 | 
			
		||||
				default: '',
 | 
			
		||||
				placeholder: 'user@example.com',
 | 
			
		||||
				displayOptions: {
 | 
			
		||||
					show: {
 | 
			
		||||
						operation: ['sendEmail'],
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				description: 'Comma-separated list of recipient email addresses',
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'Subject',
 | 
			
		||||
				name: 'subject',
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				required: true,
 | 
			
		||||
				default: '',
 | 
			
		||||
				placeholder: 'Hello from n8n!',
 | 
			
		||||
				displayOptions: {
 | 
			
		||||
					show: {
 | 
			
		||||
						operation: ['sendEmail'],
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'HTML',
 | 
			
		||||
				name: 'htmlBody',
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				required: true,
 | 
			
		||||
				default: '',
 | 
			
		||||
				typeOptions: {
 | 
			
		||||
					multiline: true,
 | 
			
		||||
				},
 | 
			
		||||
				placeholder: '<p>Your HTML content here</p>',
 | 
			
		||||
				displayOptions: {
 | 
			
		||||
					show: {
 | 
			
		||||
						operation: ['sendEmail'],
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'Text',
 | 
			
		||||
				name: 'textBody',
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				default: '',
 | 
			
		||||
				placeholder: 'Your plain text content here',
 | 
			
		||||
				displayOptions: {
 | 
			
		||||
					show: {
 | 
			
		||||
						operation: ['sendEmail'],
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				description: 'Plain text content of the email (optional)',
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'CC',
 | 
			
		||||
				name: 'ccEmail',
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				default: '',
 | 
			
		||||
				placeholder: 'cc@example.com',
 | 
			
		||||
				displayOptions: {
 | 
			
		||||
					show: {
 | 
			
		||||
						operation: ['sendEmail'],
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				description: 'Comma-separated list of CC email addresses',
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'BCC',
 | 
			
		||||
				name: 'bccEmail',
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				default: '',
 | 
			
		||||
				placeholder: 'bcc@example.com',
 | 
			
		||||
				displayOptions: {
 | 
			
		||||
					show: {
 | 
			
		||||
						operation: ['sendEmail'],
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				description: 'Comma-separated list of BCC email addresses',
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'Reply To',
 | 
			
		||||
				name: 'replyToEmail',
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				default: '',
 | 
			
		||||
				placeholder: 'reply@example.com',
 | 
			
		||||
				displayOptions: {
 | 
			
		||||
					show: {
 | 
			
		||||
						operation: ['sendEmail'],
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				description: 'Email address to set as reply-to',
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'Tags',
 | 
			
		||||
				name: 'tags',
 | 
			
		||||
				type: 'fixedCollection',
 | 
			
		||||
				typeOptions: {
 | 
			
		||||
					multipleValues: true,
 | 
			
		||||
				},
 | 
			
		||||
				default: {},
 | 
			
		||||
				placeholder: 'Add Tag',
 | 
			
		||||
				displayOptions: {
 | 
			
		||||
					show: {
 | 
			
		||||
						operation: ['sendEmail'],
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				properties: [
 | 
			
		||||
					{
 | 
			
		||||
						name: 'tag',
 | 
			
		||||
						displayName: 'Tag',
 | 
			
		||||
						values: [
 | 
			
		||||
							{
 | 
			
		||||
								displayName: 'Name',
 | 
			
		||||
								name: 'name',
 | 
			
		||||
								type: 'string',
 | 
			
		||||
								default: '',
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								displayName: 'Value',
 | 
			
		||||
								name: 'value',
 | 
			
		||||
								type: 'string',
 | 
			
		||||
								default: '',
 | 
			
		||||
							},
 | 
			
		||||
						],
 | 
			
		||||
					},
 | 
			
		||||
				],
 | 
			
		||||
				description: 'Tags to categorize the email',
 | 
			
		||||
			},
 | 
			
		||||
		],
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
 | 
			
		||||
		const items = this.getInputData();
 | 
			
		||||
		const returnData: INodeExecutionData[] = [];
 | 
			
		||||
		const length = items.length;
 | 
			
		||||
 | 
			
		||||
		for (let i = 0; i < length; i++) {
 | 
			
		||||
			try {
 | 
			
		||||
				const operation = this.getNodeParameter('operation', i) as string;
 | 
			
		||||
				const credentials = await this.getCredentials('resendApi');
 | 
			
		||||
				const apiKey = credentials.apiKey as string;
 | 
			
		||||
 | 
			
		||||
				if (operation === 'sendEmail') {
 | 
			
		||||
					const fromEmail = this.getNodeParameter('fromEmail', i) as string;
 | 
			
		||||
					const toEmail = this.getNodeParameter('toEmail', i) as string;
 | 
			
		||||
					const subject = this.getNodeParameter('subject', i) as string;
 | 
			
		||||
					const htmlBody = this.getNodeParameter('htmlBody', i) as string;
 | 
			
		||||
					const textBody = this.getNodeParameter('textBody', i, '') as string;
 | 
			
		||||
					const ccEmail = this.getNodeParameter('ccEmail', i, '') as string;
 | 
			
		||||
					const bccEmail = this.getNodeParameter('bccEmail', i, '') as string;
 | 
			
		||||
					const replyToEmail = this.getNodeParameter('replyToEmail', i, '') as string;
 | 
			
		||||
					const tagsData = this.getNodeParameter('tags', i, { tag: [] }) as { tag: Array<{ name: string; value: string }> };
 | 
			
		||||
 | 
			
		||||
					const requestBody: any = {
 | 
			
		||||
						from: fromEmail,
 | 
			
		||||
						to: toEmail.split(',').map(email => email.trim()).filter(email => email),
 | 
			
		||||
						subject: subject,
 | 
			
		||||
						html: htmlBody,
 | 
			
		||||
					};
 | 
			
		||||
 | 
			
		||||
					if (textBody) {
 | 
			
		||||
						requestBody.text = textBody;
 | 
			
		||||
					}
 | 
			
		||||
					if (ccEmail) {
 | 
			
		||||
						requestBody.cc = ccEmail.split(',').map(email => email.trim()).filter(email => email);
 | 
			
		||||
					}
 | 
			
		||||
					if (bccEmail) {
 | 
			
		||||
						requestBody.bcc = bccEmail.split(',').map(email => email.trim()).filter(email => email);
 | 
			
		||||
					}
 | 
			
		||||
					if (replyToEmail) {
 | 
			
		||||
						requestBody.reply_to = replyToEmail;
 | 
			
		||||
					}
 | 
			
		||||
					if (tagsData.tag && tagsData.tag.length > 0) {
 | 
			
		||||
						requestBody.tags = tagsData.tag.map(t => ({ name: t.name, value: t.value }));
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					const response = await this.helpers.httpRequest({
 | 
			
		||||
						url: 'https://api.resend.com/emails',
 | 
			
		||||
						method: 'POST',
 | 
			
		||||
						headers: {
 | 
			
		||||
							Authorization: `Bearer ${apiKey}`,
 | 
			
		||||
							'Content-Type': 'application/json',
 | 
			
		||||
						},
 | 
			
		||||
						body: requestBody,
 | 
			
		||||
						json: true,
 | 
			
		||||
					});
 | 
			
		||||
 | 
			
		||||
					returnData.push({ json: response, pairedItem: { item: i } });
 | 
			
		||||
				}
 | 
			
		||||
			} catch (error) {
 | 
			
		||||
				if (this.continueOnFail()) {
 | 
			
		||||
					returnData.push({ json: { error: error.message }, pairedItem: { item: i } });
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
				throw new NodeOperationError(this.getNode(), error);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return [returnData];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue