{
  "id": "3bsNBJ5i1ylNYtKM",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Automated Recruitment Rejection Emails with Google Sheets and Gmail at End-of-Day",
  "tags": [],
  "nodes": [
    {
      "id": "afba507f-8023-4c22-af01-d040e266d257",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 18 * * *"
            },
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "0f99da8f-7974-4fed-986c-e1985d4ad486",
      "name": "Set: Config",
      "type": "n8n-nodes-base.set",
      "position": [
        220,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "1e96273a-d891-44c4-b433-2a11840b8c63",
              "name": "SPREADSHEET_ID",
              "type": "string",
              "value": "1n_AIqOd10Q0ErQZSO4q4LBMekwgsR4cP7EW2q9nEzdk"
            },
            {
              "id": "0aae4a91-f153-4493-a9a8-9a2023e4f158",
              "name": "SOURCE_SHEET",
              "type": "string",
              "value": "Candidate Status"
            },
            {
              "id": "802cb08e-cfec-4a40-9236-afea62476136",
              "name": "TIMEZONE",
              "type": "string",
              "value": "Asia/Kolkata"
            },
            {
              "id": "708470ef-5283-4440-9db7-4bd9336ed468",
              "name": "REJECT_STATUS_CSV",
              "type": "string",
              "value": "Rejected"
            },
            {
              "id": "fbaed34b-a618-45ec-883c-7ee89dc2932f",
              "name": "SMTP_FROM",
              "type": "string",
              "value": "user@example.com"
            },
            {
              "id": "b3f745cb-b5d2-43e9-a7a1-803fc0646274",
              "name": "SUBJECT_TEMPLATE",
              "type": "string",
              "value": "Regarding your application for {{role}} at {{company_name}}"
            },
            {
              "id": "03bb79a9-6115-402d-a88a-de24bdaf6016",
              "name": "HTML_TEMPLATE",
              "type": "string",
              "value": "<p>Dear {{candidate_name}},</p><p>Thank you for your interest in the {{role}} position at {{company_name}} and for taking the time to interview with our team.</p><p>After careful consideration, we have decided to move forward with another candidate for this role. This decision was not easy, as we were impressed by your qualifications and experience.</p>{{feedback_html}}<p>We encourage you to apply for future opportunities that align with your skills and interests. We will keep your resume on file for consideration.</p><p>Thank you again for your time and interest in {{company_name}}.</p><p>Best regards,<br>{{recruiter_name}}<br>{{company_name}} Recruiting Team</p>"
            },
            {
              "id": "7fa7d589-8e71-4de4-80bc-fe8bd2c91f1f",
              "name": "TEXT_TEMPLATE",
              "type": "string",
              "value": "Dear {{candidate_name}},\\n\\nThank you for your interest in the {{role}} position at {{company_name}} and for taking the time to interview with our team.\\n\\nAfter careful consideration, we have decided to move forward with another candidate for this role. This decision was not easy, as we were impressed by your qualifications and experience.\\n\\n{{feedback_text}}\\n\\nWe encourage you to apply for future opportunities that align with your skills and interests. We will keep your resume on file for consideration.\\n\\nThank you again for your time and interest in {{company_name}}.\\n\\nBest regards,\\n{{recruiter_name}}\\n{{company_name}} Recruiting Team"
            },
            {
              "id": "881304fe-b862-4d6d-a4e5-8c774fcbb341",
              "name": "RATE_LIMIT_SECONDS",
              "type": "number",
              "value": 10
            },
            {
              "id": "4355e287-bbe4-4e82-a2bd-1cd086dc79db",
              "name": "INCLUDE_WEEKENDS",
              "type": "boolean",
              "value": true
            },
            {
              "id": "74a3dd62-e07d-4878-8ca6-ea6c5fc603d8",
              "name": "DRY_RUN",
              "type": "boolean",
              "value": false
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "504591fe-f969-41cc-87a2-b472916b0d52",
      "name": "Check Weekend Policy",
      "type": "n8n-nodes-base.if",
      "position": [
        420,
        -20
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "3f4bd624-1242-4949-a6dc-f1916a35998c",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.INCLUDE_WEEKENDS }}",
              "rightValue": true
            },
            {
              "id": "11590e43-b563-4652-ba39-c554bbdde5c8",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ [1, 2, 3, 4, 5].includes(new Date().getDay()) }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "da3d050e-cf10-474b-aced-685cdbff2eb8",
      "name": "Weekend Skip Message",
      "type": "n8n-nodes-base.set",
      "position": [
        640,
        80
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "957a03f9-9552-4023-af3b-a315f437ef92",
              "name": "message",
              "type": "string",
              "value": "Skipping execution - Weekend and INCLUDE_WEEKENDS is false"
            },
            {
              "id": "40645baf-356e-4a1f-964a-87248a29205b",
              "name": "timestamp",
              "type": "string",
              "value": "={{ new Date().toISOString() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "ee2c781f-9147-4361-aef7-ed2206f51274",
      "name": "Filter Candidates for Rejection",
      "type": "n8n-nodes-base.code",
      "position": [
        920,
        -140
      ],
      "parameters": {
        "jsCode": "// Get configuration from the Set node\nconst config = $('Set: Config').item.json;\nconst rejectStatuses = config.REJECT_STATUS_CSV.split(',').map(s => s.trim());\n\n// Filter candidates that need rejection emails\nconst candidatesToEmail = [];\nconst skippedCandidates = [];\n\nfor (const item of $input.all()) {\n  const data = item.json;\n  \n  // Skip if no candidate email\n  if (!data.candidate_email || data.candidate_email.trim() === '') {\n    skippedCandidates.push({ reason: 'No email', data });\n    continue;\n  }\n  \n  // Skip if rejection already sent\n  if (data.rejection_sent_at && data.rejection_sent_at.trim() !== '') {\n    skippedCandidates.push({ reason: 'Already sent', data });\n    continue;\n  }\n  \n  // Check if status matches rejection criteria (case-sensitive)\n  if (!rejectStatuses.includes(data.status)) {\n    skippedCandidates.push({ reason: 'Status not in reject list', data });\n    continue;\n  }\n  \n  // Skip if missing required fields\n  if (!data.candidate_name || !data.role || !data.company_name || !data.recruiter_name) {\n    skippedCandidates.push({ reason: 'Missing required fields', data });\n    continue;\n  }\n  \n  candidatesToEmail.push(data);\n}\n\nconsole.log(`Found ${candidatesToEmail.length} candidates to email`);\nconsole.log(`Skipped ${skippedCandidates.length} candidates`);\n\n// Return the filtered candidates\nreturn candidatesToEmail.map(candidate => ({ json: candidate }));"
      },
      "typeVersion": 2
    },
    {
      "id": "ad8780bc-7ff2-490c-9c28-437d70002d0d",
      "name": "Is Dry Run?",
      "type": "n8n-nodes-base.if",
      "position": [
        1360,
        -227.38767390945867
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "d47f5a55-07e4-4e27-96c5-ad18c94aa4f5",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $('Set: Config').item.json.DRY_RUN }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "7b40df04-318a-4a0f-884f-071ea173e7e0",
      "name": "Dry Run Preview",
      "type": "n8n-nodes-base.code",
      "position": [
        1580,
        -316.6968033946768
      ],
      "parameters": {
        "jsCode": "// Dry run preview\nconst config = $('Set: Config').item.json;\nconst candidates = $input.all();\n\nconsole.log('DRY RUN MODE - Preview of emails to be sent:');\nconsole.log('='.repeat(50));\n\nfor (let i = 0; i < candidates.length; i++) {\n  const candidate = candidates[i].json;\n  \n  // Process subject template\n  let subject = config.SUBJECT_TEMPLATE\n    .replace(/{{candidate_name}}/g, candidate.candidate_name || '')\n    .replace(/{{role}}/g, candidate.role || '')\n    .replace(/{{company_name}}/g, candidate.company_name || '')\n    .replace(/{{recruiter_name}}/g, candidate.recruiter_name || '');\n  \n  console.log(`${i + 1}. To: ${candidate.candidate_email}`);\n  console.log(`   Subject: ${subject}`);\n  console.log(`   Status: ${candidate.status}`);\n  console.log(`   Role: ${candidate.role}`);\n  console.log('');\n}\n\nconsole.log(`Total emails that would be sent: ${candidates.length}`);\nconsole.log('='.repeat(50));\n\nreturn [{ json: { \n  message: `DRY RUN: Would send ${candidates.length} rejection emails`, \n  candidates_count: candidates.length,\n  dry_run: true\n}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "47770189-90de-4470-af74-a3a0ce25c842",
      "name": "Process Email Template",
      "type": "n8n-nodes-base.code",
      "position": [
        1580,
        -120
      ],
      "parameters": {
        "jsCode": "// Process email template for current candidate\nconst config = $('Set: Config').item.json;\nconst candidate = $json;\n\n// Process feedback if available\nlet feedbackHtml = '';\nlet feedbackText = '';\nif (candidate.interview_feedback && candidate.interview_feedback.trim() !== '') {\n  feedbackHtml = `<p><strong>Feedback:</strong> ${candidate.interview_feedback}</p>`;\n  feedbackText = `\\nFeedback: ${candidate.interview_feedback}\\n`;\n}\n\n// Process HTML template\nlet htmlBody = config.HTML_TEMPLATE\n  .replace(/{{candidate_name}}/g, candidate.candidate_name || '')\n  .replace(/{{role}}/g, candidate.role || '')\n  .replace(/{{company_name}}/g, candidate.company_name || '')\n  .replace(/{{recruiter_name}}/g, candidate.recruiter_name || '')\n  .replace(/{{feedback_html}}/g, feedbackHtml);\n\n// Process text template\nlet textBody = config.TEXT_TEMPLATE\n  .replace(/{{candidate_name}}/g, candidate.candidate_name || '')\n  .replace(/{{role}}/g, candidate.role || '')\n  .replace(/{{company_name}}/g, candidate.company_name || '')\n  .replace(/{{recruiter_name}}/g, candidate.recruiter_name || '')\n  .replace(/{{feedback_text}}/g, feedbackText);\n\n// Process subject template\nlet subject = config.SUBJECT_TEMPLATE\n  .replace(/{{candidate_name}}/g, candidate.candidate_name || '')\n  .replace(/{{role}}/g, candidate.role || '')\n  .replace(/{{company_name}}/g, candidate.company_name || '')\n  .replace(/{{recruiter_name}}/g, candidate.recruiter_name || '');\n\nreturn {\n  json: {\n    ...candidate,\n    email_subject: subject,\n    email_html: htmlBody,\n    email_text: textBody,\n    smtp_from: config.SMTP_FROM\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "03cba26e-e096-4e03-9b51-2337da836e1e",
      "name": "Has Candidates to Email?",
      "type": "n8n-nodes-base.if",
      "position": [
        1140,
        -140
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "30b45fed-d929-4718-ac41-21a96c295295",
              "operator": {
                "type": "object",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "62b25b62-a7fb-4f89-9929-52f0bd95eb82",
      "name": "No Candidates Message",
      "type": "n8n-nodes-base.set",
      "position": [
        1380,
        60
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d879c53b-b757-48a8-bc98-12ab841d83a8",
              "name": "message",
              "type": "string",
              "value": "No candidates found for rejection emails today"
            },
            {
              "id": "ef1e7563-4483-44a8-b8fd-cf2b25f82e8a",
              "name": "timestamp",
              "type": "string",
              "value": "={{ new Date().toISOString() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "71d973fc-3439-4141-8d15-92a3809ea4ca",
      "name": "Rate Limit Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        1800,
        -120
      ],
      "parameters": {
        "amount": "={{ $('Set: Config').first().json.RATE_LIMIT_SECONDS }}\n"
      },
      "typeVersion": 1.1
    },
    {
      "id": "502e0718-c576-4200-9e70-813dc8f00ee1",
      "name": "Log Success",
      "type": "n8n-nodes-base.set",
      "position": [
        2400,
        -120
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "0b708662-5515-443b-9cdc-42c9552f07ad",
              "name": "message",
              "type": "string",
              "value": "=Rejection email sent successfully to {{ $json.candidate_email }}"
            },
            {
              "id": "47d6070a-3cf4-4693-b807-fadc4203e41b",
              "name": "candidate_name",
              "type": "string",
              "value": "={{ $json.candidate_name }}"
            },
            {
              "id": "d34acf55-9411-4ee0-96c9-f764dbeabcbc",
              "name": "sent_at",
              "type": "string",
              "value": "={{ new Date().toISOString() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "926bc390-d721-4b13-add3-7bc508a8f0ba",
      "name": "Mark as Sent in Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2220,
        -120
      ],
      "parameters": {
        "columns": {
          "value": {
            "rejection_sent_at": "={{ new Date().toISOString() }}"
          },
          "schema": [
            {
              "id": "candidate_name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "candidate_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "candidate_email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "candidate_email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "role",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "role",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "recruiter_name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "recruiter_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "rejection_sent_at",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "rejection_sent_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "candidate_email"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Candidate Status"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "1n_AIqOd10Q0ErQZSO4q4LBMekwgsR4cP7EW2q9nEzdk"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "52bba329-067e-46a5-aef4-7f28558b16ac",
      "name": "Send Rejection Email1",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2020,
        -120
      ],
      "parameters": {
        "sendTo": "={{$json.candidate_email}}",
        "message": "={{$json.email_html}}",
        "options": {},
        "subject": "={{$json.email_subject}}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "958eb49a-a567-4608-be1f-52b5291bdbf6",
      "name": "Fetch Candidate Data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        700,
        -140
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Candidate Status"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "1n_AIqOd10Q0ErQZSO4q4LBMekwgsR4cP7EW2q9nEzdk"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6,
      "alwaysOutputData": true
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "77ed4c82-e3b6-4b6e-b67f-b6cd686f3fcc",
  "connections": {
    "Is Dry Run?": {
      "main": [
        [
          {
            "node": "Dry Run Preview",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Process Email Template",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set: Config": {
      "main": [
        [
          {
            "node": "Check Weekend Policy",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit Wait": {
      "main": [
        [
          {
            "node": "Send Rejection Email1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Set: Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Weekend Policy": {
      "main": [
        [
          {
            "node": "Fetch Candidate Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Weekend Skip Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Candidate Data": {
      "main": [
        [
          {
            "node": "Filter Candidates for Rejection",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark as Sent in Sheet": {
      "main": [
        [
          {
            "node": "Log Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Rejection Email1": {
      "main": [
        [
          {
            "node": "Mark as Sent in Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Email Template": {
      "main": [
        [
          {
            "node": "Rate Limit Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Candidates to Email?": {
      "main": [
        [
          {
            "node": "Is Dry Run?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Candidates Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Candidates for Rejection": {
      "main": [
        [
          {
            "node": "Has Candidates to Email?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}