{
  "id": "FFWaoXQ5I0HX8xN1",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Email sending and tracking",
  "tags": [],
  "nodes": [
    {
      "id": "60885e38-6e19-4bf4-899a-e793a22d98e4",
      "name": "Tracking Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -896,
        640
      ],
      "parameters": {
        "path": "email-open-track",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "0f3f1f16-7ad8-4685-921a-26364e45b1a1",
      "name": "Set Pixel And Metadata",
      "type": "n8n-nodes-base.set",
      "position": [
        -560,
        640
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "2c3b4d7a-b64a-42be-b775-6ca4097673b1",
              "name": "query.email",
              "type": "string",
              "value": "={{ $json.query.email }}"
            },
            {
              "id": "85187f32-6bc7-403b-b2cf-22e7cb45927d",
              "name": "query.name",
              "type": "string",
              "value": "={{ $json.query.name }}"
            },
            {
              "id": "016d7890-74fe-43e8-be49-af052a7bde33",
              "name": "query.subject",
              "type": "string",
              "value": "={{ $json.query.subject }}"
            },
            {
              "id": "b0eecc2b-e955-409b-940f-1c384957215a",
              "name": "headers[\"x-real-ip\"]",
              "type": "string",
              "value": "={{ $json.headers[\"x-real-ip\"] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f368b045-5e9c-4b74-a2c1-143ed92759e5",
      "name": "Send Open Notification",
      "type": "n8n-nodes-base.gmail",
      "position": [
        112,
        640
      ],
      "parameters": {
        "sendTo": "patelravi7210@gmail.com",
        "message": "=Tracking pixel was loaded.\n\nEmail: {{ $('Set Pixel And Metadata').item.json.query.email }}\n\nSubject: {{ $('Set Pixel And Metadata').item.json.query.subject }}\n\nOpened at: {{ $json.currentDate }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "=Email opened:{{ $('Set Pixel And Metadata').item.json.query.email }} subject {{ $('Set Pixel And Metadata').item.json.query.subject }}",
        "emailType": "text"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "520cc970-0d9d-48b8-aa7b-b05ffd0f7904",
      "name": "When chat message received",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "position": [
        -896,
        -192
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1.4
    },
    {
      "id": "b7e0db14-5894-4193-a8a0-c7689c117b1d",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -608,
        -192
      ],
      "parameters": {
        "text": "=You are an expert cold email writer.\n\nYour task:\nI will give you only an ACTION at last as action, company profile URL, LinkedIn profile URL, website URL, job post URL, or any text context.\n\nYou must automatically:\n\n1. Extract the recipient's name.\n2. Identify their company, role, industry, and relevant context.\n3. Understand the action I want to take.\n4. Create a personalized cold email.\n5. Use simple, natural English.\n6. Avoid AI-sounding language, buzzwords, hype, and generic compliments.\n7. Keep the email between 90 and 120 words.\n8. Include only one clear CTA.\n9. Personalize the first sentence based on the provided context.\n10. Sign the email as:\n    Your Name\n\nOutput format:\n\nRecipient Name: [Name]\nEmail: [Email if available, otherwise N/A]\n\nSubject: [Subject Line]\n\nEmail Body:\n[Complete email]\n\nRules:\n\n* No markdown.\n* No bullet points inside the email.\n* No emojis.\n* No fake information.\n* If recipient details are missing, infer as much as possible from the provided context.\n* Make the email sound like it was written by a real person.\n* Focus on relevance, not selling.\n* Return only the final formatted output.\naction:  {{ $json.chatInput }}\n",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 2
    },
    {
      "id": "cabd8f4a-99b1-4da6-8d3a-b3bc7d20838c",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -608,
        -16
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemma-4-31b-it"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "61a8ef14-c654-4651-b3a6-5c6741452d23",
      "name": "Parse AI Output",
      "type": "n8n-nodes-base.code",
      "disabled": true,
      "position": [
        -192,
        -192
      ],
      "parameters": {
        "jsCode": "const raw = $json.output || $json.text || '';\n\nconst getValue = (label) => {\n  const match = raw.match(new RegExp(`${label}:\\\\s*([\\\\s\\\\S]*?)(?=\\\\n(?:Recipient Name|Email|Subject|Email Body):|$)`, 'i'));\n  return match ? match[1].trim() : '';\n};\n\nconst recipientName = getValue('Recipient Name');\nconst toEmail = getValue('Email');\nconst subjectFromAi = getValue('Subject');\nconst textBody = getValue('Email Body');\n\nif (!toEmail || toEmail.toLowerCase() === 'n/a') {\n  throw new Error('Recipient email missing in AI output');\n}\n\nreturn [{\n  json: {\n    chatInput: $json.chatInput,\n    recipientName,\n    toEmail,\n    subject: subjectFromAi || `Quick idea for ${recipientName || 'there'}`,\n    textBody\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "277aa578-e3f2-4647-b912-3ff800547ee3",
      "name": "Generate Tracking ID",
      "type": "n8n-nodes-base.code",
      "position": [
        128,
        -192
      ],
      "parameters": {
        "jsCode": "const crypto = require('crypto');\nconst trackingId = crypto.randomUUID();\nconst subject = $json.subject || 'Quick idea';\nconst trackingUrl = `\"webhook url from tracking webhook node\"?id=${encodeURIComponent(trackingId)}&email=${encodeURIComponent($json.toEmail)}&name=${encodeURIComponent($json.recipientName || '')}&subject=${encodeURIComponent(subject)}`;\nreturn [{ json: { ...$json, trackingId, trackingUrl } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "d07fff12-3bac-40f0-85fa-098f6ef1f1e9",
      "name": "Build HTML Email",
      "type": "n8n-nodes-base.code",
      "position": [
        496,
        -192
      ],
      "parameters": {
        "jsCode": "const escapeHtml = (str='') => str\n  .replace(/&/g, '&amp;')\n  .replace(/</g, '&lt;')\n  .replace(/>/g, '&gt;')\n  .replace(/\\\"/g, '&quot;')\n  .replace(/'/g, '&#39;');\n\nconst paragraphs = ($json.textBody || '')\n  .split(/\\n\\s*\\n/)\n  .filter(Boolean)\n  .map(p => `<p>${escapeHtml(p).replace(/\\n/g, '<br>')}</p>`)\n  .join('');\n\nconst htmlBody = `<div style=\"font-family:Arial,sans-serif;line-height:1.6;color:#222;max-width:600px;\">${paragraphs}<img src=\"${$json.trackingUrl}\" width=\"1\" height=\"1\" alt=\"\" style=\"display:block;border:0;width:1px;height:1px;\" /></div>`;\n\nreturn [{ json: { ...$json, htmlBody, sentAt: new Date().toISOString() } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "6312abdf-b288-4826-96bd-a0ee13be6b49",
      "name": "Send via Gmail",
      "type": "n8n-nodes-base.gmail",
      "position": [
        768,
        -192
      ],
      "parameters": {
        "sendTo": "={{$json.toEmail}}",
        "message": "={{$json.htmlBody}}",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{$json.subject}}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "5b1cda78-237b-4e47-9dc1-a980b1db189d",
      "name": "Date & Time",
      "type": "n8n-nodes-base.dateTime",
      "position": [
        -192,
        640
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "32a3da49-c503-42cf-b95e-e83b27659976",
      "name": "Sticky Note Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1744,
        -336
      ],
      "parameters": {
        "color": 5,
        "width": 460,
        "height": 272,
        "content": "## Cold Email + Open Tracking\nThis workflow generates a personalized cold email from chat input, parses the AI output, creates a tracking URL, builds an HTML email, and sends it via Gmail.\n\nSecond branch: when the tracking pixel URL is loaded, the webhook captures the recipient metadata and sends you an internal open notification.\n\nNote: These sticky notes are documentation only and do not affect execution."
      },
      "typeVersion": 1
    },
    {
      "id": "b80925e8-d07c-4501-a544-0b4553d90cfc",
      "name": "Sticky - Chat Trigger",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -992,
        -416
      ],
      "parameters": {
        "color": 7,
        "width": 260,
        "height": 410,
        "content": "## Chat Trigger\nPaste your action, LinkedIn URL, company URL, website, job post, or plain text context here.\n\nThis starts the outreach flow and passes the user input to the AI Agent."
      },
      "typeVersion": 1
    },
    {
      "id": "251eae44-d368-4bd3-b93a-c7de0d7ee3e3",
      "name": "Sticky - Tracking Webhook",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -992,
        416
      ],
      "parameters": {
        "color": 7,
        "width": 300,
        "height": 388,
        "content": "## Tracking Webhook\nThis webhook is called when the tracking pixel URL is loaded from the recipient\u2019s email client.\n\nIt reads query parameters like email, name, and subject, then passes the event into the notification branch."
      },
      "typeVersion": 1
    },
    {
      "id": "30fcbe91-3048-4957-a534-f1efc294dc74",
      "name": "Sticky - Set Pixel Metadata",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -640,
        448
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 393,
        "content": "## Set Pixel And Metadata\nThis node normalizes webhook data so the next nodes can use clean values.\n\nIt extracts tracked fields such as recipient email, recipient name, subject, and request IP header."
      },
      "typeVersion": 1
    },
    {
      "id": "250d51a0-26ab-48b5-ba39-263d21e99923",
      "name": "Sticky - Open Notification",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        16,
        448
      ],
      "parameters": {
        "color": 7,
        "width": 280,
        "height": 383,
        "content": "## Open Notification\nThis Gmail node sends an internal alert when the tracking pixel is requested.\n\nUse it to monitor email opens with the recipient email, subject, and timestamp."
      },
      "typeVersion": 1
    },
    {
      "id": "b61648f6-5ccb-41e8-8173-b5337f0728e9",
      "name": "Sticky - AI Agent",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -672,
        -416
      ],
      "parameters": {
        "color": 7,
        "width": 310,
        "height": 560,
        "content": "## AI Agent\nThis node writes the personalized cold email based on the incoming context.\n\nIt is expected to return four labeled sections: Recipient Name, Email, Subject, and Email Body.\n\nIn prompt add your name"
      },
      "typeVersion": 1
    },
    {
      "id": "4ee415dc-bfff-42bf-92e1-05ee04bf2562",
      "name": "Sticky - Parse AI Output",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        -448
      ],
      "parameters": {
        "color": 7,
        "width": 264,
        "height": 490,
        "content": "## Parse AI Output\nThis node extracts structured fields from the AI response.\n\nIt expects predictable labels and throws an error if no recipient email is found."
      },
      "typeVersion": 1
    },
    {
      "id": "c4bbe331-9538-40d3-8164-7bc961987420",
      "name": "Sticky - Generate Tracking ID",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        48,
        -448
      ],
      "parameters": {
        "color": 7,
        "width": 284,
        "height": 484,
        "content": "## Generate Tracking ID\nThis node creates a unique tracking ID and appends it to the tracking URL.\n\nReplace the placeholder webhook URL with your real production webhook URL before using the workflow."
      },
      "typeVersion": 1
    },
    {
      "id": "e5617b86-61c3-4755-b16e-3a0def9f6def",
      "name": "Sticky - Build HTML Email",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        384,
        -448
      ],
      "parameters": {
        "color": 7,
        "width": 300,
        "height": 474,
        "content": "## Build HTML Email\nThis node converts the plain email body into HTML paragraphs and appends the tracking pixel image.\n\nThe output is used directly by the Gmail send node."
      },
      "typeVersion": 1
    },
    {
      "id": "52da3d73-1b87-400e-9b21-53d4d3a6a714",
      "name": "Sticky - Send via Gmail",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        720,
        -448
      ],
      "parameters": {
        "color": 7,
        "width": 280,
        "height": 447,
        "content": "## Send via Gmail\nThis node sends the final personalized email to the extracted recipient email address.\n\nIt uses the subject from the AI output and the HTML body created in the previous step."
      },
      "typeVersion": 1
    },
    {
      "id": "dbdd96df-4f58-419b-9b9c-5ee4f9c8604a",
      "name": "Sticky - Date Time",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -272,
        448
      ],
      "parameters": {
        "color": 7,
        "width": 250,
        "height": 389,
        "content": "## Date & Time\nThis node adds the current timestamp to the webhook event.\n\nIt is used in the notification email so you know when the tracking pixel was loaded."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "d6821c47-b90c-4b71-a690-f4fbdba75a60",
  "nodeGroups": [],
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Parse AI Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Date & Time": {
      "main": [
        [
          {
            "node": "Send Open Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Output": {
      "main": [
        [
          {
            "node": "Generate Tracking ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build HTML Email": {
      "main": [
        [
          {
            "node": "Send via Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tracking Webhook": {
      "main": [
        [
          {
            "node": "Set Pixel And Metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Tracking ID": {
      "main": [
        [
          {
            "node": "Build HTML Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Pixel And Metadata": {
      "main": [
        [
          {
            "node": "Date & Time",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "When chat message received": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}