n8n-nodes-gmail-custom 0.3.0 → 0.4.0

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 CHANGED
@@ -1,32 +1,66 @@
1
1
  # n8n-nodes-gmail-custom
2
2
 
3
- Custom Gmail node for n8n with token caching and 429 retry.
3
+ Self-contained n8n node for sending emails via Gmail API with a Google service account and domain-wide delegation.
4
4
 
5
- A drop-in replacement for the native Gmail v2 Send operation, optimized for service account usage with domain-wide delegation.
5
+ No credential setup in n8n required all parameters are inline, supports expressions.
6
6
 
7
7
  ## Features
8
8
 
9
- - Same UI as native Gmail Send (To, Subject, Email Type, Message, Options with CC/BCC/ReplyTo/Attachments)
10
- - **Token caching** access token is reused across calls (1 auth call per hour instead of per email)
11
- - **429 retry** automatic retry with exponential backoff when rate limited
12
- - **From Email inline** specify the delegated user directly in the node (no need to set in credential)
13
- - No extra API calls (no GET /profile like the native node)
9
+ - **Self-contained**: service account email + private key are node parameters (no n8n credential needed)
10
+ - **Token caching**: access token reused for 58 minutes — only 1 oauth call per hour instead of per email
11
+ - **Domain-wide delegation**: send as any user in your Workspace domain
12
+ - **Sender name**: set a display name without extra API calls
13
+ - **Optional custom Message-ID**: auto-generated `<uuid@domain>` or specify your own included in output
14
+ - **Attachments**: from binary data with graceful skip if missing
15
+ - **HTML and plain text email support**
14
16
 
15
17
  ## Installation
16
18
 
17
- In n8n: **Settings → Community Nodes → Install** → Enter `n8n-nodes-gmail-custom`
19
+ In n8n: **Settings → Community Nodes → Install** → `n8n-nodes-gmail-custom`
18
20
 
19
21
  ## Usage
20
22
 
21
23
  1. Add a **Gmail Custom** node to your workflow
22
- 2. Select your **Google API** credential (service account)
23
- 3. Fill in:
24
- - **From Email**: the delegated user to send as (e.g. `segreteria.a@domain.com`)
25
- - **To**: recipient(s)
26
- - **Subject**: email subject
27
- - **Email Type**: Text or HTML
28
- - **Message**: email body
29
- 4. Configure advanced Options as needed (CC, BCC, ReplyTo, Attachments)
24
+ 2. Fill in the required fields (supports expressions):
25
+
26
+ | Field | Description | Example |
27
+ |---|---|---|
28
+ | Service Account Email | Your SA `client_email` | `sa-name@project.iam.gserviceaccount.com` |
29
+ | Private Key | RSA private key (PEM, hidden) | Expression from a previous node |
30
+ | From Email | The user to impersonate (DWD) | `sender@your-domain.com` |
31
+ | To | Recipient(s), comma-separated | `recipient@example.com` |
32
+ | Subject | Email subject | `Hello World` |
33
+ | Email Type | `HTML` or `Text` | `HTML` |
34
+ | Message | Email body | Your HTML or text |
35
+
36
+ 3. Configure **Options** as needed:
37
+ - **CC / BCC**: carbon copy recipients
38
+ - **Reply To**: reply-to address
39
+ - **Sender Name**: display name (e.g. `John Doe`)
40
+ - **Attachments**: binary data field names from previous nodes
41
+ - **Custom Message-ID**: enable and optionally specify a `Message-ID` header. Auto-generated as `<uuid@domain>` if left empty. Returned in the node output.
42
+
43
+ ## Output
44
+
45
+ ```json
46
+ {
47
+ "id": "19ef486e4ed521e6",
48
+ "threadId": "19ef486e4ed521e6",
49
+ "labelIds": ["SENT"],
50
+ "messageId": "<generated-uuid@your-domain.com>"
51
+ }
52
+ ```
53
+
54
+ `messageId` is only present when the Custom Message-ID option is enabled.
55
+
56
+ ## How it works
57
+
58
+ 1. Signs a JWT with the service account private key
59
+ 2. Obtains an access token from `oauth2.googleapis.com/token` (cached)
60
+ 3. Builds a MIME email message
61
+ 4. Sends via `POST gmail.googleapis.com/gmail/v1/users/me/messages/send`
62
+
63
+ Per email sent: **1 Gmail API call** (token is cached for 1 hour).
30
64
 
31
65
  ## License
32
66
 
@@ -51,6 +51,10 @@ async function buildMimeMessage(options) {
51
51
  mailOptions.html = options.htmlBody;
52
52
  }
53
53
 
54
+ if (options.messageId) {
55
+ mailOptions.messageId = options.messageId;
56
+ }
57
+
54
58
  if (options.attachments && options.attachments.length > 0) {
55
59
  mailOptions.attachments = options.attachments.map((att) => ({
56
60
  filename: att.fileName || 'attachment',
@@ -89,6 +93,7 @@ async function buildMimeMessage(options) {
89
93
  if (cc) lines.push(`CC: ${cc}`);
90
94
  if (bcc) lines.push(`BCC: ${bcc}`);
91
95
  if (replyTo) lines.push(`Reply-To: ${replyTo}`);
96
+ if (options.messageId) lines.push(`Message-ID: ${options.messageId}`);
92
97
  lines.push(`Subject: ${subject}`);
93
98
  lines.push('MIME-Version: 1.0');
94
99
 
@@ -363,6 +368,25 @@ class GmailCustom {
363
368
  placeholder: 'e.g. John Doe',
364
369
  description: 'Name shown as the sender in recipients\' inboxes',
365
370
  },
371
+ {
372
+ displayName: 'Custom Message-ID',
373
+ name: 'enableMessageId',
374
+ type: 'boolean',
375
+ default: false,
376
+ description: 'Whether to set a custom Message-ID header (auto-generated if not specified)',
377
+ },
378
+ {
379
+ displayName: 'Message-ID Value',
380
+ name: 'customMessageId',
381
+ type: 'string',
382
+ default: '',
383
+ displayOptions: {
384
+ show: {
385
+ enableMessageId: [true],
386
+ },
387
+ },
388
+ description: 'Leave empty to auto-generate a unique Message-ID',
389
+ },
366
390
  ],
367
391
  },
368
392
  ],
@@ -404,6 +428,16 @@ class GmailCustom {
404
428
  fromHeader = `${senderName} <${fromEmail}>`;
405
429
  }
406
430
 
431
+ let messageId = null;
432
+ if (options.enableMessageId) {
433
+ if (options.customMessageId) {
434
+ messageId = options.customMessageId;
435
+ } else {
436
+ const domain = fromEmail.includes('@') ? fromEmail.split('@')[1] : 'localhost';
437
+ messageId = `<${crypto.randomUUID()}@${domain}>`;
438
+ }
439
+ }
440
+
407
441
  let bodyText = message;
408
442
  let bodyHtml = '';
409
443
 
@@ -450,22 +484,27 @@ class GmailCustom {
450
484
  textBody: bodyText,
451
485
  htmlBody: bodyHtml,
452
486
  attachments,
487
+ messageId,
453
488
  });
454
489
 
455
490
  const sendResponse = await this.helpers.httpRequest({
456
- method: 'POST',
457
- url: 'https://gmail.googleapis.com/gmail/v1/users/me/messages/send',
458
- headers: {
459
- Authorization: `Bearer ${accessToken}`,
460
- 'Content-Type': 'application/json',
461
- },
462
- body: { raw: base64UrlMessage },
463
- });
491
+ method: 'POST',
492
+ url: 'https://gmail.googleapis.com/gmail/v1/users/me/messages/send',
493
+ headers: {
494
+ Authorization: `Bearer ${accessToken}`,
495
+ 'Content-Type': 'application/json',
496
+ },
497
+ body: { raw: base64UrlMessage },
498
+ });
464
499
 
465
- returnData.push({
466
- json: sendResponse,
467
- pairedItem: { item: i },
468
- });
500
+ const result = { ...sendResponse };
501
+ if (messageId) {
502
+ result.messageId = messageId;
503
+ }
504
+ returnData.push({
505
+ json: result,
506
+ pairedItem: { item: i },
507
+ });
469
508
 
470
509
  } catch (error) {
471
510
  let continueOnFail = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-gmail-custom",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Custom Gmail node for n8n with token caching and 429 retry",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",