AutomationFlowsAI & RAG › Daily Applicant Digest via Gmail & Gemini

Daily Applicant Digest via Gmail & Gemini

Original n8n title: Daily Applicant Digest

Daily-Applicant-Digest. Uses lmChatGoogleGemini, gmail, chainLlm. Scheduled trigger; 10 nodes.

Cron / scheduled trigger★★★★☆ complexityAI-powered10 nodesGoogle Gemini ChatGmailChain Llm
AI & RAG Trigger: Cron / scheduled Nodes: 10 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow follows the Chainllm → Gmail recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "MGgeJBbeovHVQAbq",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Daily-Applicant-Digest",
  "tags": [],
  "nodes": [
    {
      "id": "75fe46c9-d9eb-455e-8f9d-7384c4a1d172",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -340,
        -80
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 6
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ba41cb19-9463-4c8a-a94d-a7d579346b1e",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        120,
        120
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-1.5-flash"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "9e2ef974-5183-4e7d-9cd2-8f3872bd0a66",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -520,
        -360
      ],
      "parameters": {
        "width": 1720,
        "height": 680,
        "content": "## Send daily applicant digest by role from Gmail to hiring managers with Google Gemini"
      },
      "typeVersion": 1
    },
    {
      "id": "4648ef51-49af-40f8-9c82-490c5965e5be",
      "name": "fetch_applicant_emails",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -120,
        -80
      ],
      "parameters": {
        "filters": {
          "q": "label: applicant newer_than:1d is:unread"
        },
        "operation": "getAll"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "f479ae8a-490b-4b08-9fe3-dbe6a26b2320",
      "name": "readAll_applicant_emails",
      "type": "n8n-nodes-base.gmail",
      "position": [
        120,
        -280
      ],
      "parameters": {
        "messageId": "={{ $json.id }}",
        "operation": "markAsRead"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "2a8e9656-df75-439f-ac16-ba608b29c87d",
      "name": "Extract Applicant Details",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        100,
        -80
      ],
      "parameters": {
        "text": "=You are an assistant that extracts job applicant information from emails.\n\nExtract the following fields and return ONLY valid JSON with these keys:\n- name\n- email\n- phone\n- role\n- years_of_experience\n- top_skills\n- location\n- notice_period\n- summary\n\nApplicant email:\n\"\"\"\n{{$json.snippet }}\n\"\"\"\n",
        "batching": {},
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "3937cc5c-ea58-4b5d-b22d-591f8097481f",
      "name": "Assign Manager Emails",
      "type": "n8n-nodes-base.code",
      "position": [
        480,
        -80
      ],
      "parameters": {
        "jsCode": "function stripMarkdownJson(input) {\n  let str = (input || '').trim();\n\n  // Remove the ```json at the start and ```\n  // This regex removes triple backticks and the 'json' keyword at the start\n  str = str.replace(/^```json\\s*/, '');  // Remove starting ```\n  str = str.replace(/```$/, '');          // Remove ending ```\n  \n  return str.trim();\n}\n\nfunction normalizeRole(role) {\n  return (role || '').trim().toLowerCase();\n}\n\nconst roleToManagerEmail = {\n  'java team lead': 'user@example.com',\n  'python developer': 'user@example.com',\n  'frontend developer': 'user@example.com',\n  // add other roles as needed\n};\n\nconst fallbackManagerEmail = 'user@example.com';\n\nreturn items.map(item => {\n  const raw = item.json.text || '';\n  const jsonStr = stripMarkdownJson(raw);\n\n  let parsed;\n  try {\n    parsed = JSON.parse(jsonStr);\n  } catch (e) {\n    item.json.error = `JSON parse failed: ${e.message}`;\n    return item;\n  }\n\n  // Merge the parsed JSON fields into the current item\n  Object.assign(item.json, parsed);\n\n  // Normalize role and assign manager email\n  const normalizedRole = normalizeRole(item.json.role);\n  const managerEmail = Object.prototype.hasOwnProperty.call(roleToManagerEmail, normalizedRole)\n    ? roleToManagerEmail[normalizedRole]\n    : fallbackManagerEmail;\n\n  item.json.managerEmail = managerEmail;\n\n  return item;\n});\n"
      },
      "typeVersion": 2
    },
    {
      "id": "5943f68f-a39f-4532-a437-732d98d53079",
      "name": "Group & Build HTML Tables",
      "type": "n8n-nodes-base.code",
      "position": [
        700,
        -80
      ],
      "parameters": {
        "jsCode": "function escapeHtml(text) {\n  if (!text) return '';\n  return text\n    .replace(/&/g, \"&amp;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\")\n    .replace(/\"/g, \"&quot;\")\n    .replace(/'/g, \"&#039;\");\n}\n\n// Group applicants by managerEmail, then by role\nconst groupedByManager = {};\n\n// Step 1: Group items\nitems.forEach(item => {\n  const applicant = item.json;\n  const managerEmail = applicant.managerEmail || 'user@example.com';\n  const role = applicant.role || 'Unknown Role';\n\n  if (!groupedByManager[managerEmail]) {\n    groupedByManager[managerEmail] = {};\n  }\n  if (!groupedByManager[managerEmail][role]) {\n    groupedByManager[managerEmail][role] = [];\n  }\n  \n  groupedByManager[managerEmail][role].push(applicant);\n});\n\n// Step 2: Build HTML per manager with tables per role\nconst output = [];\n\nfor (const [managerEmail, roles] of Object.entries(groupedByManager)) {\n\n  let emailHtml = `<h2>Today's New Applicants</h2>`;\n\n  for (const [role, applicants] of Object.entries(roles)) {\n    emailHtml += `<h3>Role: ${escapeHtml(role)}</h3>`;\n    \n    // Build table header\n    emailHtml += `\n    <table border=\"1\" cellpadding=\"5\" cellspacing=\"0\" style=\"border-collapse:collapse; width: 100%;\">\n      <thead style=\"background-color:#f0f0f0;\">\n        <tr>\n          <th>Name</th>\n          <th>Email</th>\n          <th>Phone</th>\n          <th>Years of Experience</th>\n          <th>Top Skills</th>\n          <th>Location</th>\n          <th>Notice Period</th>\n          <th>Summary</th>\n        </tr>\n      </thead>\n      <tbody>\n    `;\n\n    // Add each applicant as a row\n    applicants.forEach(app => {\n      emailHtml += `\n        <tr>\n          <td>${escapeHtml(app.name)}</td>\n          <td><a href=\"mailto:${escapeHtml(app.email)}\">${escapeHtml(app.email)}</a></td>\n          <td>${escapeHtml(app.phone)}</td>\n          <td>${escapeHtml(app.years_of_experience?.toString())}</td>\n          <td>${Array.isArray(app.top_skills) ? escapeHtml(app.top_skills.join(', ')) : escapeHtml(app.top_skills)}</td>\n          <td>${escapeHtml(app.location)}</td>\n          <td>${escapeHtml(app.notice_period)}</td>\n          <td>${escapeHtml(app.summary)}</td>\n        </tr>\n      `;\n    });\n\n    emailHtml += `</tbody></table><br/>`;\n  }\n\n  // Push one item per manager, as output for the next step (email sending)\n  output.push({\n    json: {\n      managerEmail,\n      html: emailHtml\n    }\n  });\n}\n\nreturn output;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "9509d5c2-f66f-4294-9432-0b2587283b7a",
      "name": "Send Digest to Managers",
      "type": "n8n-nodes-base.gmail",
      "position": [
        900,
        -80
      ],
      "parameters": {
        "sendTo": "={{ $json.managerEmail }}",
        "message": "={{ $json.html }}",
        "options": {},
        "subject": "=Today's Applicants Digest \u2013 {{ $now.format('MM-DD') }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "17486203-2374-43f0-88eb-80500108d770",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -520,
        360
      ],
      "parameters": {
        "width": 1720,
        "height": 780,
        "content": "## Workflow Overview: Send daily applicant digest by role from Gmail to hiring managers with Google Gemini\n\n**Purpose:**\nAutomatically fetches new job application emails labeled applicants, extracts structured applicant details using OpenAI, groups candidates by role and manager, then sends a daily HTML summary email to each hiring manager.\n\nDaily Applicant Digest Workflow \u2013 Node Overview\n\n**1. Daily Trigger (6PM IST)**\n\nStarts the workflow every day at 18:00 (Asia/Kolkata timezone).\n\n**2. Fetch Applicant Emails**\n\nRetrieves all new application emails labeled applicants from the last 24 hours.\n\n**3. Read All Emails**\n\nRead each email\u2019s labeled applicants which we retrieves\n\n**4. Extract Applicant Details**\n\nUses OpenAI to extract and structure applicant info (name, email, role, skills, etc.) in JSON format.\n\n**5. Assign Manager Emails**\n\nMaps each applicant\u2019s role to a hiring manager\u2019s email address.\n\nUses a fallback email if the role does not match any manager.\n\n**6. Group & Build HTML Tables**\n\nGroups applicants by manager and role.\n\nBuilds a clear, formatted HTML table for each group, summarizing all applicants.\n\n**7. Send Digest to Managers**\n\nSends one HTML summary email per manager, listing all relevant new applicants for the day.\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "67d27489-333a-4e73-8d9c-1af780d66304",
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "fetch_applicant_emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Assign Manager Emails": {
      "main": [
        [
          {
            "node": "Group & Build HTML Tables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "fetch_applicant_emails": {
      "main": [
        [
          {
            "node": "Extract Applicant Details",
            "type": "main",
            "index": 0
          },
          {
            "node": "readAll_applicant_emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Extract Applicant Details",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Extract Applicant Details": {
      "main": [
        [
          {
            "node": "Assign Manager Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Group & Build HTML Tables": {
      "main": [
        [
          {
            "node": "Send Digest to Managers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

How this works

Streamline your recruitment process with a daily digest that compiles applicant emails into a concise, AI-generated summary delivered straight to your inbox, saving hours of manual sorting and review. This workflow suits hiring managers or small teams handling high volumes of job applications via Gmail, automating the extraction of key details like names, experiences, and contact info using Google Gemini's chat model. The core step involves chaining the LLM to parse and structure data from fetched emails, followed by grouping into HTML tables for easy scanning.

Use this workflow when you receive 10-50 applicant emails daily and need a quick overview without sifting through inboxes, especially for roles with consistent application formats. Avoid it for very high-volume recruitment requiring advanced filtering or when emails lack standardised structures, as the AI extraction may falter. Common variations include customising the cron schedule for weekly digests or adding Slack notifications instead of email delivery for faster team alerts.

About this workflow

Daily-Applicant-Digest. Uses lmChatGoogleGemini, gmail, chainLlm. Scheduled trigger; 10 nodes.

Source: https://github.com/weblineindia/n8n-Daily-applicant-digest-by-role-with-Gemini-AI-extraction-for-hiring-managers/blob/main/main.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

kisisel asistan. Uses toolWorkflow, toolHttpRequest, toolCalculator, toolThink. Scheduled trigger; 43 nodes.

Tool Workflow, Tool Http Request, Tool Calculator +15
AI & RAG

Categories Content Creation AI Automation Publishing Social Media

Google Docs, HTTP Request, Slack +7
AI & RAG

Automatically identifies overdue sales leads and generates personalized follow-up emails using AI. Runs every weekday Reads leads from Google Sheets Filters leads with no contact for 5+ days Downloads

Google Sheets, Chain Llm, Google Gemini Chat +3
AI & RAG

Fetch user-specific research papers from arXiv on a daily schedule, process and structure the data, and create or update entries in a Notion database, with support for data delivery Paper Topic: singl

Chain Llm, Google Gemini Chat, HTTP Request +3
AI & RAG

This workflow automatically analyzes competitor websites, which inseted in description field and generates a clean, structured AI‑powered Battle Card for every new Zoho CRM deal. It reads the competit

HTTP Request, Google Gemini Chat, Output Parser Structured +3