This workflow corresponds to n8n.io template #11912 — we link there as the canonical source.
This workflow follows the Agent → Airtable 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 →
{
"id": "Svef8eVAN9KUcsvD",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Automated Hiring Assistant",
"tags": [
{
"id": "2V3HXFbv2wqNGm6s",
"name": "Dev",
"createdAt": "2025-06-17T05:42:41.949Z",
"updatedAt": "2025-06-17T05:42:41.949Z"
}
],
"nodes": [
{
"id": "084dbfb5-5dc1-4c80-b719-bd5a900ea9db",
"name": "Gmail Trigger1",
"type": "n8n-nodes-base.gmailTrigger",
"position": [
704,
1200
],
"parameters": {
"simple": false,
"filters": {},
"options": {
"downloadAttachments": true,
"dataPropertyAttachmentsPrefixName": "data"
},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
}
},
"typeVersion": 1.3
},
{
"id": "a74d22f5-4f92-49fe-ac64-a07cfe4ede4c",
"name": "Message a model2",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
928,
1200
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini",
"cachedResultName": "GPT-4.1-MINI"
},
"options": {},
"messages": {
"values": [
{
"content": "=You are an email classifier AI agent. Your ONLY task is to determine if an email is a job application.\n\nAnalyze the email subject and body, then respond with ONLY \"YES\" or \"NO\".\n\nRESPOND \"YES\" if the email shows ANY of these signs:\n- Explicitly mentions applying for a position/job/role/opening\n- Contains phrases like \"I am interested in\", \"I would like to apply\", \"submitting my application\", \"I'm reaching out regarding the position\"\n- Includes resume/CV attachment mentions or links to portfolio/LinkedIn\n- References job posting, job listing, or career opportunity\n- Discusses qualifications, experience, or skills in context of seeking employment\n- Mentions availability for interview or joining date\n- Uses language like \"I am a good fit\", \"my background aligns\", \"I believe I would be\"\n- Contains salary expectations or notice period\n- References job boards (LinkedIn, Indeed, Naukri, etc.) where they found the listing\n\nRESPOND \"NO\" if the email is:\n- Sales/marketing pitch\n- Business proposal or partnership request\n- Customer support inquiry\n- Personal message or networking without job intent\n- Recruitment agency offering candidates (not applying themselves)\n- Newsletter or automated message\n- Meeting request unrelated to job application\n- General inquiry about company/services\n- Internal company communication\n- Spam or promotional content\n\nEMAIL SUBJECT: {{ $json.subject }}\n\nEMAIL BODY: {{ $json.textAsHtml }}\n\nRESPONSE (YES or NO): "
}
]
}
},
"typeVersion": 1.8
},
{
"id": "81d94a85-64bc-4f5b-b837-d2f97d8cbe45",
"name": "If2",
"type": "n8n-nodes-base.if",
"position": [
1280,
1200
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "enter your-id-here",
"operator": {
"type": "string",
"operation": "notEquals"
},
"leftValue": "={{ $json.message.content }}",
"rightValue": "NO"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "4f54b1fd-5fee-4759-b679-f79f3b075a62",
"name": "Get a message1",
"type": "n8n-nodes-base.gmail",
"position": [
1504,
1200
],
"parameters": {
"simple": false,
"options": {
"downloadAttachments": true,
"dataPropertyAttachmentsPrefixName": "data"
},
"messageId": "={{ $('Gmail Trigger1').item.json.id }}",
"operation": "get"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "11e4c6d5-4fca-4bce-83bb-550786d625f5",
"name": "Upload file1",
"type": "n8n-nodes-base.googleDrive",
"position": [
1728,
1104
],
"parameters": {
"name": "=Resume - {{ $json.from.value[0].address }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive",
"cachedResultUrl": "enter your url here",
"cachedResultName": "My Drive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "list",
"value": "enter your value here",
"cachedResultUrl": "enter your url here",
"cachedResultName": "Attachments"
},
"inputDataFieldName": "data0"
},
"typeVersion": 3
},
{
"id": "f70b96b3-ecb6-4531-87dd-2601711c8426",
"name": "Extract from File1",
"type": "n8n-nodes-base.extractFromFile",
"position": [
1728,
1296
],
"parameters": {
"options": {},
"operation": "pdf",
"binaryPropertyName": "data0"
},
"typeVersion": 1
},
{
"id": "23c0d969-9cc2-42e8-b619-3c58ce8aca72",
"name": "Message a model3",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
2176,
1296
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini",
"cachedResultName": "GPT-4.1-MINI"
},
"options": {},
"messages": {
"values": [
{
"content": "=You are a resume analysis AI agent. Analyze the resume text and determine the best role fit with a score out of 10.\n\nRESUME TEXT:\n{{ $('Extract from File1').item.json.text }}\n\nAVAILABLE POSITIONS:\n{{ $json['Position 1'] }}\n{{ $json['position 2'] }}\n{{ $json['Position 3'] }}\n\nYOUR TASK:\n1. Analyze the candidate's skills, experience, education, and background\n2. Match them ONLY to roles from the AVAILABLE POSITIONS list above\n3. Assign a fit score from 1-10 based on how well they match the AVAILABLE roles\n4. IMPORTANT: If the candidate's profile doesn't match ANY available position, assign a low score (1-3) even if they are qualified for other roles not in the list\n\nSCORING CRITERIA:\n10/10 = Perfect match - All key requirements met for an AVAILABLE position, highly relevant experience\n9/10 = Excellent match - Most requirements met for an AVAILABLE position, strong relevant experience\n8/10 = Very good match - Core requirements met for an AVAILABLE position, good experience\n7/10 = Good match - Main requirements met for an AVAILABLE position, some gaps in experience\n6/10 = Above average - Several requirements met for an AVAILABLE position, moderate experience\n5/10 = Average match - Basic requirements met for an AVAILABLE position, limited relevant experience\n4/10 = Below average - Few requirements met for AVAILABLE positions, minimal experience\n3/10 = Poor match - Qualified but for roles NOT in available positions list\n2/10 = Very poor match - Skills don't align with any available position\n1/10 = No match - Completely unqualified for all available positions\n\nCRITICAL RULE: Even if candidate is highly qualified (e.g., expert Frontend Developer), if that role is NOT in the available positions list, maximum score is 3/10. They must match an AVAILABLE position to score higher.\n\nANALYZE FOR:\n- Years of relevant experience\n- Technical skills match\n- Industry experience\n- Education/certifications alignment\n- Project/achievement relevance\n- Tool/technology proficiency\n- Soft skills indicators\n- Career trajectory fit\n\nRESPONSE FORMAT (JSON ONLY, NO MARKDOWN):\n{\n \"recommended_role\": \"Exact role name from available roles\",\n \"fit_score\": 8,\n \"key_strengths\": \"Summary of all strengths\",\n \"gaps\": \"Summary of all gaps\",\n \"years_of_experience\": 5,\n \"top_skills\": \"Summary of all skills\",\n \"reasoning\": \"Brief explanation of why this role and score (2-3 sentences)\"\n}\n\nReturn ONLY the JSON object with no additional text or markdown formatting."
}
]
},
"jsonOutput": true
},
"typeVersion": 1.8
},
{
"id": "d684b149-b83e-4e07-ad71-0c5887d1bd7a",
"name": "Create a record2",
"type": "n8n-nodes-base.airtable",
"position": [
4176,
1200
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "enter your value here",
"cachedResultUrl": "enter your url here",
"cachedResultName": "Untitled Base"
},
"table": {
"__rl": true,
"mode": "list",
"value": "enter your value here",
"cachedResultUrl": "enter your url here",
"cachedResultName": "Table 1"
},
"columns": {
"value": {
"Gaps": "={{ $('Message a model3').item.json.message.content.gaps }}",
"Name": "={{ $('Get a message1').item.json.from.value[0].name }}",
"Email": "={{ $('Get a message1').item.json.from.value[0].address }}",
"Score": "={{ $('Message a model3').item.json.message.content.fit_score }}",
"Skills": "={{ $('Message a model3').item.json.message.content.top_skills }}",
"Reasoning": "={{ $('Message a model3').item.json.message.content.reasoning }}",
"Strengths": "={{ $('Message a model3').item.json.message.content.key_strengths }}",
"Experience": "={{ $('Message a model3').item.json.message.content.years_of_experience }}",
"Recommended Role": "={{ $('Message a model3').item.json.message.content.recommended_role }}"
},
"schema": [
{
"id": "Name",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Recommended Role",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Recommended Role",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Score",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Strengths",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Strengths",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Gaps",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Gaps",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Experience",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Experience",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Skills",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Skills",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Reasoning",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Reasoning",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "create"
},
"typeVersion": 2.1
},
{
"id": "689eecb2-45d4-4074-a76b-f137c2a943a4",
"name": "Available Positions1",
"type": "n8n-nodes-base.set",
"position": [
1952,
1296
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "enter your id here",
"name": "Position 1",
"type": "string",
"value": "Automation Engineer"
},
{
"id": "enter your id here",
"name": "position 2",
"type": "string",
"value": "Full Stack Developer"
},
{
"id": "enter your id here",
"name": "Position 3",
"type": "string",
"value": "Javascript Developer"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "dcd5dfd0-d4ed-43b9-9595-36bc19d68903",
"name": "If3",
"type": "n8n-nodes-base.if",
"position": [
2528,
1296
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "enter your id here",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $json.message.content.fit_score }}",
"rightValue": 8
}
]
}
},
"typeVersion": 2.2
},
{
"id": "0a09a53f-b764-4c17-9367-ceac0ebfba83",
"name": "Create a record3",
"type": "n8n-nodes-base.airtable",
"position": [
2752,
1392
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "app8cAHXTF3J6QsLX",
"cachedResultUrl": "https://airtable.com/app8cAHXTF3J6QsLX",
"cachedResultName": "Untitled Base"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblYA2GaJtqhOXhfm",
"cachedResultUrl": "https://airtable.com/app8cAHXTF3J6QsLX/tblYA2GaJtqhOXhfm",
"cachedResultName": "Table 1"
},
"columns": {
"value": {
"Gaps": "={{ $json.message.content.gaps }}",
"Name": "={{ $('Get a message1').item.json.from.value[0].name }}",
"Email": "={{ $('Get a message1').item.json.from.value[0].address }}",
"Score": "={{ $json.message.content.fit_score }}",
"Skills": "={{ $json.message.content.top_skills }}",
"Reasoning": "={{ $json.message.content.reasoning }}",
"Strengths": "={{ $json.message.content.key_strengths }}",
"Experience": "={{ $json.message.content.years_of_experience }}",
"Recommended Role": "={{ $json.message.content.recommended_role }}"
},
"schema": [
{
"id": "Name",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Recommended Role",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Recommended Role",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Score",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Strengths",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Strengths",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Gaps",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Gaps",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Experience",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Experience",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Skills",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Skills",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Reasoning",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Reasoning",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "create"
},
"typeVersion": 2.1
},
{
"id": "445957a3-8c2f-4e71-b254-41e19d99babf",
"name": "Get Next Business Day1",
"type": "n8n-nodes-base.code",
"position": [
2752,
1200
],
"parameters": {
"jsCode": "// Get Next Business Day and produce timestamps for the 9:00-18:00 window\nlet date = new Date();\n// Move to tomorrow\ndate.setDate(date.getDate() + 1);\n\n// Weekend skip logic (0=Sun,6=Sat)\nconst day = date.getDay();\nif (day === 6) {\n date.setDate(date.getDate() + 2); // Sat -> Mon\n} else if (day === 0) {\n date.setDate(date.getDate() + 1); // Sun -> Mon\n}\n\n// Format helper\nfunction pad(n) {\n return n < 10 ? '0' + n : n;\n}\n\nconst yyyy = date.getFullYear();\nconst mm = pad(date.getMonth() + 1);\nconst dd = pad(date.getDate());\n\n// Build timestamps EXACTLY like \"2025-12-10 09:00:00\"\nconst windowStart = ${yyyy}-${mm}-${dd} 09:00:00;\nconst windowEnd = ${yyyy}-${mm}-${dd} 18:00:00;\n\nconst defaultSlotStart = ${yyyy}-${mm}-${dd} 09:00:00;\nconst defaultSlotEnd = ${yyyy}-${mm}-${dd} 10:00:00;\n\nreturn [\n {\n json: {\n nextBusinessDay: ${yyyy}-${mm}-${dd},\n windowStart,\n windowEnd,\n defaultSlotStart,\n defaultSlotEnd\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "9f9af0fd-f0ae-4b22-8dd2-06598adb3bb3",
"name": "AI Agent1",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
3120,
1088
],
"parameters": {
"text": "=You are an availability checking agent.\n\n- nextBusinessDay: {{$json.nextBusinessDay}}\n\nThis is the ONLY date you must use.\n\nGOAL:\n\nYour job is to:\n1. Check availability on nextBusinessDay\n2. Find the FIRST free 1-hour slot between 9:00 AM and 6:00 PM\n3. Return only ONE timeslot (the earliest available)\n\nYou must NEVER ask the user questions.\nYou must NEVER wait for user input.\nEverything must be automatic.\n\nCHECK AVAILABILITY TOOL RULES:\n\n1. First, call the \"Get Events\" tool to fetch all events booked on nextBusinessDay.\n\n2. Then, call the \"Check Availability\" tool using the events retrieved.\n Look for slots with at least 1 hour of free time between 09:00 and 18:00.\n\nOUTPUT RULES:\n\nAfter checking availability:\n- Return ONLY the FIRST available 1-hour timeslot\n- Format EXACTLY like this:\n Start Time: 2025-12-09 09:00:00\n End Time: 2025-12-09 10:00:00\n- Use the date from {{$json.nextBusinessDay}}\n- If no slots available, say: \"No available slots found for {{$json.nextBusinessDay}}\"\n- Do NOT show JSON unless specified\n- Always output in this specific format\n{\n\t\"start_time\": \"\",\n\t\"end_time\": \"\"\n}\n- Do NOT list multiple slots - just ONE slot\n- MUST use format: YYYY-MM-DD HH:MM:SS\n\nEverything must be fully automatic.",
"options": {},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 2.2
},
{
"id": "567a5764-3b70-4571-a434-14f729f1fb3a",
"name": "OpenAI Chat Model2",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
3008,
1344
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {}
},
"typeVersion": 1.2
},
{
"id": "92f2a4d0-f8de-4353-92a2-39585527e9c3",
"name": "Check Availability1",
"type": "n8n-nodes-base.googleCalendarTool",
"position": [
3152,
1344
],
"parameters": {
"options": {
"timezone": {
"__rl": true,
"mode": "list",
"value": "Asia/Kolkata",
"cachedResultName": "Asia/Kolkata"
}
},
"timeMax": "={{ $('Get Next Business Day1').item.json.windowEnd }}",
"timeMin": "={{ $('Get Next Business Day1').item.json.windowStart }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "user@example.com",
"cachedResultName": "user@example.com"
},
"resource": "calendar"
},
"typeVersion": 1.3
},
{
"id": "c29e4f5a-6776-4918-9113-e67e241a0d89",
"name": "Create an event1",
"type": "n8n-nodes-base.googleCalendar",
"position": [
3728,
1200
],
"parameters": {
"end": "={{ $json.output.end_time }}",
"start": "={{ $json.output.start_time }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "user@example.com",
"cachedResultName": "user@example.com"
},
"additionalFields": {
"summary": "Interview Scheduled with Dev Doshi",
"location": "iTechNotion Pvt Ltd Office, Makarba",
"attendees": [
"={{ $('Get a message1').item.json.to.value[0].address }}",
"={{ $('Get a message1').item.json.from.value[0].address }}"
],
"description": "=Interview for Role - {{ $('If3').item.json.message.content.recommended_role }}"
},
"useDefaultReminders": false
},
"typeVersion": 1.3
},
{
"id": "7f20d8cc-2336-485e-a95b-92351162939a",
"name": "Structured Output Parser1",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
3392,
1392
],
"parameters": {
"autoFix": true,
"jsonSchemaExample": "{\n\t\"start_time\": \"\",\n\t\"end_time\": \"\"\n}"
},
"typeVersion": 1.3
},
{
"id": "b2fe478a-e68b-4248-b4e1-321402abcc3a",
"name": "Send a message1",
"type": "n8n-nodes-base.gmail",
"position": [
3952,
1200
],
"parameters": {
"sendTo": "={{ $('Gmail Trigger1').item.json.from.value[0].address }}",
"message": "=<p>Dear {{ $('Get a message1').item.json.from.value[0].name }},</p>\n\n<p>Thank you for your interest in the <strong>{{ $('If3').item.json.message.content.recommended_role }}</strong> role at <strong>iTechNotion</strong>. We have reviewed your resume and are impressed with your qualifications and experience.</p>\n\n<p>Based on your profile evaluation, we would like to invite you for an interview to discuss this opportunity further.</p>\n\n<p><strong>\ud83d\udcc5 Interview Details:</strong></p>\n<p>\n Date: <strong>{{ $json.start.dateTime.split('T')[0] }}</strong><br>\n Time: <strong>{{ ((s=new Date($json.start.dateTime)) => ((e=new Date($json.end.dateTime)) => ((h=x=>x%12||12)(s.getHours()) + '-' + h(e.getHours()) + ' ' + (s.getHours()>=12?'PM':'AM')))() )() }}</strong><br>\n Duration: <strong>1 Hour</strong><br>\n Location: <strong>{{ $json.location }}</strong>\n</p>\n\n<p>We look forward to speaking with you and learning more about how your skills align with our team's needs.</p>\n\n<p>Best regards,<br>\niTechNotion<br>\n079 4894 8998\n</p>\n",
"options": {
"appendAttribution": false
},
"subject": "=Interview Scheduled for - {{ $json.start.dateTime.split('T')[0] }}"
},
"typeVersion": 2.1
},
{
"id": "966519e3-4215-4976-b029-a4e219439034",
"name": "OpenAI Chat Model3",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
3488,
1536
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {},
"builtInTools": {}
},
"typeVersion": 1.3
},
{
"id": "8982ba1f-c45b-4b53-b5a0-31b5d2694921",
"name": "Get Events1",
"type": "n8n-nodes-base.googleCalendarTool",
"position": [
3280,
1344
],
"parameters": {
"options": {},
"timeMax": "={{ $json.windowEnd }}",
"timeMin": "={{ $json.windowStart }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "user@example.com",
"cachedResultName": "user@example.com"
},
"operation": "getAll",
"returnAll": true
},
"typeVersion": 1.3
},
{
"id": "745c531c-c4a2-43f0-be38-b9de93bc894a",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
880
],
"parameters": {
"width": 624,
"height": 784,
"content": "## AI-Powered Resume Screening and Interview Scheduling Agent\nThis workflow automates your entire candidate intake process\u2014from detecting job applications to evaluating resumes with AI, storing results in Airtable, scheduling interviews, and sending confirmation emails.\n\n### How it works\n- Monitors your Gmail inbox and identifies whether an incoming email is a job application using an AI classifier.\n- Retrieves the email, downloads the resume attachment, and extracts its text.\n- AI analyzes the resume against predefined open roles and returns the best-fit role, score, strengths, gaps, skills, and experience.\n- Saves all candidate details into Airtable for tracking.\n- If the candidate score meets your threshold (\u2265 8), the workflow finds the next business day and detects the earliest available 1-hour slot in Google Calendar.\n- Books the interview automatically and invites both the candidate and interviewer.\n- Sends a personalized confirmation email containing interview details.\n\n### Setup steps\n1. Connect your Gmail account for both receiving applications and sending interview confirmations.\n2. Set your Google Drive credentials to store uploaded resumes.\n3. Connect Google Calendar for availability checking and event creation.\n4. Add your Airtable base and table credentials for storing candidate evaluations.\n5. Configure the \u201cAvailable Positions\u201d Set node with the roles you are hiring for.\n6. Connect your OpenAI account for both classification and resume-analysis steps.\n7. Review the AI prompt instructions to ensure they match your hiring logic.\n8. Turn the workflow ON and send a test job-application email with a resume attachment."
},
"typeVersion": 1
},
{
"id": "2cfb9561-ba27-4249-93ce-b382a4259e4c",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
656,
880
],
"parameters": {
"color": 7,
"width": 1232,
"height": 784,
"content": "## Step 1: Detect and collect job-application data\nThe workflow monitors your Gmail inbox for new emails and uses AI to identify job applications. For valid applications, it retrieves the message, saves any attached resumes to Google Drive, and extracts PDF text for further AI analysis."
},
"typeVersion": 1
},
{
"id": "14d3f8a4-a057-4f38-a93b-ac61191c9c10",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
1904,
880
],
"parameters": {
"color": 7,
"width": 1024,
"height": 784,
"content": "## Step 2: Analyze the resume and evaluate candidate fit with AI\nOnce the resume text is extracted, the workflow passes it to an AI resume-analysis model together with your predefined list of available positions. The AI returns a JSON object containing the recommended role, fit score (1\u201310), strengths, gaps, experience, and skills."
},
"typeVersion": 1
},
{
"id": "09ca63f5-f5fb-4140-bf98-306a75e3f013",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
2944,
880
],
"parameters": {
"color": 7,
"width": 720,
"height": 784,
"content": "## Step 3: Check availability on the next business day\nFor qualified candidates, it finds the next business day and uses an AI Agent to select the earliest available one-hour slot between 9 AM and 6 PM."
},
"typeVersion": 1
},
{
"id": "4d3eb54b-6e59-4ef6-9486-5999acd4040c",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
3680,
880
],
"parameters": {
"color": 7,
"width": 688,
"height": 784,
"content": "## Step 4: Schedule the interview and notify the candidate\nOnce a slot is found, the workflow creates a Google Calendar event, invites the interviewer and candidate, and sends a personalized confirmation email with the details."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "a9a8a205-6a5c-4cae-a1ee-d4eedf644322",
"connections": {
"If2": {
"main": [
[
{
"node": "Get a message1",
"type": "main",
"index": 0
}
]
]
},
"If3": {
"main": [
[
{
"node": "Get Next Business Day1",
"type": "main",
"index": 0
}
],
[
{
"node": "Create a record3",
"type": "main",
"index": 0
}
]
]
},
"AI Agent1": {
"main": [
[
{
"node": "Create an event1",
"type": "main",
"index": 0
}
]
]
},
"Get Events1": {
"ai_tool": [
[
{
"node": "AI Agent1",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get a message1": {
"main": [
[
{
"node": "Upload file1",
"type": "main",
"index": 0
},
{
"node": "Extract from File1",
"type": "main",
"index": 0
}
]
]
},
"Gmail Trigger1": {
"main": [
[
{
"node": "Message a model2",
"type": "main",
"index": 0
}
]
]
},
"Send a message1": {
"main": [
[
{
"node": "Create a record2",
"type": "main",
"index": 0
}
]
]
},
"Create an event1": {
"main": [
[
{
"node": "Send a message1",
"type": "main",
"index": 0
}
]
]
},
"Message a model2": {
"main": [
[
{
"node": "If2",
"type": "main",
"index": 0
}
]
]
},
"Message a model3": {
"main": [
[
{
"node": "If3",
"type": "main",
"index": 0
}
]
]
},
"Extract from File1": {
"main": [
[
{
"node": "Available Positions1",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model2": {
"ai_languageModel": [
[
{
"node": "AI Agent1",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"OpenAI Chat Model3": {
"ai_languageModel": [
[
{
"node": "Structured Output Parser1",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Check Availability1": {
"ai_tool": [
[
{
"node": "AI Agent1",
"type": "ai_tool",
"index": 0
}
]
]
},
"Available Positions1": {
"main": [
[
{
"node": "Message a model3",
"type": "main",
"index": 0
}
]
]
},
"Get Next Business Day1": {
"main": [
[
{
"node": "AI Agent1",
"type": "main",
"index": 0
}
]
]
},
"Structured Output Parser1": {
"ai_outputParser": [
[
{
"node": "AI Agent1",
"type": "ai_outputParser",
"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.
gmailOAuth2
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
The workflow detects incoming job-application emails, extracts resumes, and parses them for AI analysis. It evaluates each candidate against three open roles and assigns a fit score with structured reasoning. Low-scoring applicants are stored for review, while strong candidates…
Source: https://n8n.io/workflows/11912/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This n8n workflow automates sales processes using AI agents integrated with Airtable as a CRM and Gmail for email handling. It consists of two main workflows: one for handling Airtable status changes
mails2notion V2. Uses lmChatOpenAi, toolCalculator, outputParserStructured, gmail. Event-driven trigger; 38 nodes.
This workflow automatically creates Tasks from forwarded Emails, similar to Asana, but better. Emails are processed by AI and converted to rather actionable task.
HR Job Posting and Evaluation with AI. Uses formTrigger, airtable, googleDrive, stickyNote. Event-driven trigger; 36 nodes.
HR Job Posting and Evaluation with AI. Uses formTrigger, airtable, googleDrive, stickyNote. Event-driven trigger; 36 nodes.