This workflow corresponds to n8n.io template #17172 — we link there as the canonical source.
This workflow follows the Airtable → Chainllm 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": "6523lsLdCkgMzCEt",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "AI Resume Screening",
"tags": [],
"nodes": [
{
"id": "f370d5a1-7627-488d-9109-1764a3cd2001",
"name": "Gmail Trigger",
"type": "n8n-nodes-base.gmailTrigger",
"position": [
-1488,
96
],
"parameters": {
"filters": {},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
}
},
"typeVersion": 1.3
},
{
"id": "926a1c11-312c-4f89-97d9-a227bf364a46",
"name": "Lookup Existing Candidate",
"type": "n8n-nodes-base.airtable",
"notes": "alwaysOutputData=true is REQUIRED - Airtable Search returns 0 items when no match, which would stop the IF node from firing.",
"position": [
-1232,
96
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appNbeTFT2LnRqII8",
"cachedResultName": "Recruiting Pipeline"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblkcb4TO982L23kz",
"cachedResultName": "Candidates"
},
"options": {},
"operation": "search",
"filterByFormula": "={{ \"{Email} = '\" + $json.Email + \"'\" }}"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1,
"alwaysOutputData": true
},
{
"id": "8d708e86-8daa-447c-8ff2-393e58af33a7",
"name": "If Candidate Is New",
"type": "n8n-nodes-base.if",
"notes": "TRUE = new candidate (no match found). FALSE = duplicate.",
"position": [
-1008,
96
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c1",
"operator": {
"type": "string",
"operation": "empty",
"singleValue": true
},
"leftValue": "={{ $json.id }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "9bea9a48-f929-4a47-a3fa-c3a1fd53c688",
"name": "Link to Duplicate Record",
"type": "n8n-nodes-base.airtable",
"notes": "Duplicate path - append role to existing candidate instead of creating a stranger record.",
"position": [
-784,
416
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appNbeTFT2LnRqII8",
"cachedResultName": "Recruiting Pipeline"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblkcb4TO982L23kz",
"cachedResultName": "Candidates"
},
"columns": {
"value": {},
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
]
},
"options": {},
"operation": "update"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "4d00c3c9-a7c6-416a-a856-803c63e2649f",
"name": "Create New Candidate Record",
"type": "n8n-nodes-base.airtable",
"notes": "Pulls Name/Email/Role from the form trigger explicitly - Airtable search nodes upstream do not pass through original trigger data.",
"position": [
-784,
0
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appNbeTFT2LnRqII8",
"cachedResultName": "Recruiting Pipeline"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblkcb4TO982L23kz",
"cachedResultName": "Candidates"
},
"columns": {
"value": {
"Name": "={{ $('When Form Is Submitted').item.json.Name }}",
"Role": "={{ $('When Form Is Submitted').item.json.Role }}",
"Email": "={{ $('When Form Is Submitted').item.json.Email }}",
"Stage": "New",
"Source": "Form",
"Applied At": "={{ $now.toISO() }}"
},
"mappingMode": "defineBelow"
},
"options": {},
"operation": "create"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "c6cb36ef-8dd6-47bc-92f2-e29787343488",
"name": "Reattach Resume Binary Data",
"type": "n8n-nodes-base.code",
"notes": "Airtable Create does not pass through the original trigger's binary. Pulls the resume file back from the form trigger.",
"position": [
-560,
0
],
"parameters": {
"jsCode": "return [{\n json: $input.item.json,\n binary: $('When Form Is Submitted').item.binary\n}];"
},
"typeVersion": 2
},
{
"id": "d786a45f-cd81-4b1a-bfa9-92730aa41cf3",
"name": "Extract Resume Text via LlamaParse",
"type": "@llamaindex/n8n-nodes-llamacloud.llamaParsePlatform",
"notes": "SETUP REQUIRED: install community package @llamaindex/n8n-nodes-llamacloud, add LlamaParse API credential.",
"position": [
-352,
0
],
"parameters": {
"inputDataFieldName": "Resume"
},
"credentials": {
"llamaParseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "8914d436-bae6-428b-b741-e1f7d160dad8",
"name": "Evaluate Parse Quality",
"type": "n8n-nodes-base.code",
"notes": "Flags likely mis-parsed resumes for human review instead of auto-scoring low.",
"position": [
-128,
0
],
"parameters": {
"jsCode": "const text = ($input.item.json.text || '').trim();\nconst wordCount = text ? text.split(/\\s+/).length : 0;\nconst hasEmail = /[\\w.+-]+@[\\w-]+\\.[\\w.-]+/.test(text);\nconst poor = wordCount < 80 || !hasEmail;\nreturn [{ json: { ...$input.item.json, parse_quality: poor ? 'poor' : 'good', word_count: wordCount } }];"
},
"typeVersion": 2
},
{
"id": "857eb9ce-455a-469b-bc99-4ab9ac860be7",
"name": "If Parse Quality Acceptable",
"type": "n8n-nodes-base.if",
"position": [
96,
0
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "c1",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.parse_quality }}",
"rightValue": "good"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "4a8f1863-d63c-4f15-9321-30b12424a1c3",
"name": "Flag Candidate for Manual Review",
"type": "n8n-nodes-base.airtable",
"position": [
320,
240
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appNbeTFT2LnRqII8",
"cachedResultName": "Recruiting Pipeline"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblkcb4TO982L23kz",
"cachedResultName": "Candidates"
},
"columns": {
"value": {
"id": "={{ $('Create New Candidate Record').item.json.id }}",
"Stage": "Needs Manual Review",
"Parse Quality": "poor"
},
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
]
},
"options": {},
"operation": "update"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "af1be229-2a46-4eb9-8324-b0b3d0c35843",
"name": "Fetch Role Rubric from Airtable",
"type": "n8n-nodes-base.airtable",
"notes": "Fetches scoring rubric for THIS role from Roles table. alwaysOutputData prevents silent breaks on no-match.",
"position": [
320,
-336
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appNbeTFT2LnRqII8",
"cachedResultName": "Recruiting Pipeline"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblBpNb2t8gW7PdDC",
"cachedResultName": "Roles"
},
"options": {},
"operation": "search",
"filterByFormula": "={{ \"{Role} = '\" + $('Create New Candidate Record').item.json.fields.Role + \"'\" }}"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1,
"alwaysOutputData": true
},
{
"id": "dc3d0850-162a-48ea-8827-8cad8173af20",
"name": "Sanitize and Clean Resume Text",
"type": "n8n-nodes-base.code",
"notes": "Strips prompt-injection chars. Handles Airtable's inconsistent shape: Search flattens fields, Create/Update nest under .fields.",
"position": [
544,
-336
],
"parameters": {
"jsCode": "let text = $('Evaluate Parse Quality').item.json.text || '';\ntext = text.replace(/[\\u200B-\\u200D\\uFEFF]/g, '');\ntext = text.replace(/\\s+/g, ' ').trim();\nif (text.length > 12000) text = text.slice(0, 12000);\n\nconst src = $input.item.json;\nconst raw = src.fields || src;\nconst rubric = {\n Role: raw['Role'],\n 'Must Haves': raw['Must Haves'],\n 'Nice to Haves': raw['Nice to Haves'],\n 'Weights / Notes': raw['Weights / Notes']\n};\n\nreturn [{ json: { resume_text: text, rubric } }];"
},
"typeVersion": 2
},
{
"id": "8b500458-0357-4858-9a63-76aae03663d0",
"name": "Screen Candidate with AI",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
768,
-336
],
"parameters": {
"text": "=You are a hiring screener. Score the candidate ONLY against the rubric provided. Return ONLY valid JSON, no prose, no markdown fences.\n\nRUBRIC:\n{{ JSON.stringify($json.rubric) }}\n\nRESUME:\n{{ $json.resume_text }}\n\nReturn JSON with exactly these keys: overall_score (0-100 integer), sub_scores (object of criterion:score 0-10), criteria_met (array of {criterion, status: met|not_met|unclear}), red_flags (array of strings), reasoning (2-3 sentences), confidence (low|medium|high), recommendation (advance|review|reject).",
"promptType": "define"
},
"typeVersion": 1.5
},
{
"id": "a551cfd3-8660-46ec-b634-8d0e342a880c",
"name": "DeepSeek Screening Model",
"type": "@n8n/n8n-nodes-langchain.lmChatDeepSeek",
"position": [
704,
-128
],
"parameters": {
"model": "deepseek-v4-flash",
"options": {}
},
"credentials": {
"deepSeekApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "39bcfcea-1364-4672-97a3-628f9b976ee9",
"name": "Validate AI Score Output",
"type": "n8n-nodes-base.code",
"position": [
992,
-336
],
"parameters": {
"jsCode": "let raw = $input.item.json.text || $input.item.json.response || '';\nraw = raw.replace(/```json|```/g, '').trim();\nlet parsed;\ntry { parsed = JSON.parse(raw); } catch(e) {\n return [{ json: { valid: false, overall_score: 0, confidence: 'low', recommendation: 'review', reasoning: 'AI returned malformed output - needs human review', red_flags: ['parse_error'] } }];\n}\nparsed.overall_score = Math.max(0, Math.min(100, parseInt(parsed.overall_score) || 0));\nif (!parsed.confidence) parsed.confidence = 'low';\nparsed.valid = true;\nreturn [{ json: parsed }];"
},
"typeVersion": 2
},
{
"id": "0b46902f-8488-40df-9910-8ebc4450f4dd",
"name": "Save Screening Score to Airtable",
"type": "n8n-nodes-base.airtable",
"position": [
1200,
-336
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appNbeTFT2LnRqII8",
"cachedResultName": "Recruiting Pipeline"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblkcb4TO982L23kz",
"cachedResultName": "Candidates"
},
"columns": {
"value": {
"id": "={{ $('Create New Candidate Record').item.json.id }}",
"Score": "={{ $json.overall_score }}",
"Stage": "Awaiting Review",
"Reasoning": "={{ $json.reasoning }}",
"Red Flags": "={{ ($json.red_flags || []).join(', ') }}",
"Confidence": "={{ $json.confidence }}",
"AI Recommendation": "={{ $json.recommendation }}"
},
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
]
},
"options": {},
"operation": "update"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "5829713d-1129-4b07-afdc-9e1db7a756b7",
"name": "Send for Human Review",
"type": "@gotohuman/n8n-nodes-gotohuman.gotoHuman",
"notes": "SETUP REQUIRED: install community package @gotohuman/n8n-nodes-gotohuman, add gotoHuman API credential, select your Review Board template, map fields, set Review Data to {{ $json }}.",
"position": [
1424,
-96
],
"parameters": {
"agentID": {
"__rl": true,
"mode": "list",
"value": "Q6gB0zy9DEwGhDQaTWHA",
"cachedResultName": "New agent (Q6gB0zy9DEwGhDQaTWHA)"
},
"reviewData": "={{ $json }}",
"additionalFields": {},
"reviewTemplateID": {
"__rl": true,
"mode": "list",
"value": "KizIUHWt7tPnBg3B5qDm",
"cachedResultName": "My Review Step (KizIUHWt7tPnBg3B5qDm)"
}
},
"credentials": {
"gotoHumanApi": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "1f5275ad-e292-47f7-9271-c1139710a6a8",
"name": "Route by Human Decision",
"type": "n8n-nodes-base.if",
"notes": "TRUE = Advance. FALSE = Reject.",
"position": [
1648,
-96
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c1",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.reviewResult.response }}",
"rightValue": "approved"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "cfcaa7ec-49e3-47fe-82b0-9f6281c7ced3",
"name": "Update Record as Advanced",
"type": "n8n-nodes-base.airtable",
"notes": "Sets Awaiting Interview flag for future WF-02 to pick up.",
"position": [
1984,
-96
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appNbeTFT2LnRqII8",
"cachedResultName": "Recruiting Pipeline"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblkcb4TO982L23kz",
"cachedResultName": "Candidates"
},
"columns": {
"value": {
"id": "={{ $('Create New Candidate Record').item.json.id }}",
"Stage": "Advanced",
"Awaiting Interview": true
},
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
]
},
"options": {},
"operation": "update"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "1860b9b6-2ab2-4abe-a1a2-6f4fc3f1c020",
"name": "Update Record as Rejected",
"type": "n8n-nodes-base.airtable",
"position": [
1808,
32
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appNbeTFT2LnRqII8",
"cachedResultName": "Recruiting Pipeline"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblkcb4TO982L23kz",
"cachedResultName": "Candidates"
},
"columns": {
"value": {
"id": "={{ $('Create New Candidate Record').item.json.id }}",
"Stage": "Rejected"
},
"mappingMode": "defineBelow",
"matchingColumns": [
"id"
]
},
"options": {},
"operation": "update"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "6a6a06bf-cad4-41d1-ae2d-a81694614e51",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2112,
-464
],
"parameters": {
"width": 480,
"height": 896,
"content": "# AI Resume Screening\n\n### How it works\n\n1. The workflow is triggered when a candidate submits a job application form via email with their resume.\n2. It checks if the candidate already exists in Airtable and creates a new record if they are new, or links the submission to an existing record if they are a duplicate.\n3. It attempts to parse the resume text using LlamaParse and validates the parsing quality.\n4. If parsing fails, the candidate is flagged for manual review; if successful, the candidate's resume is screened against a role-specific rubric using a DeepSeek AI model.\n5. The AI generates a structured score which is validated and saved to Airtable, then sent to a human reviewer for a final advance or reject decision.\n\n### Setup steps\n\n- [x] Configure **Airtable** credentials and ensure your base has tables for Candidates and Roles with matching field names.\n- [x] Set up a **LlamaParse** account and add your API key to the LlamaParse node for resume text extraction.\n- [x] Configure the **DeepSeek Chat Model** within the Screen Candidate with AI node by adding your DeepSeek API key.\n- [x] Set up a **gotoHuman** account and configure the Send for Human Review node with your workspace and review template.\n- [x] Ensure a **public webhook URL** (ngrok or hosted n8n) is set - required for the human-approval step to receive responses.\n\n### Customization\n\nYou can customize the AI screening rubric by modifying the prompt in the Screen Candidate with AI node to match your specific role requirements. The parsing quality threshold in the Evaluate Parse Quality node can also be adjusted."
},
"typeVersion": 1
},
{
"id": "4e05c4c5-714d-4938-97da-374fac6d6800",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1552,
-112
],
"parameters": {
"color": 7,
"height": 384,
"content": "## Email trigger for incoming applications\n\nCollects Name, Email, Role, and Resume upload to start the screening process."
},
"typeVersion": 1
},
{
"id": "16f16437-08f5-4770-929a-212d82bb5ec2",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1280,
-64
],
"parameters": {
"color": 7,
"width": 416,
"height": 320,
"content": "## Check for existing candidate\n\nQueries Airtable to see if the candidate already exists and branches based on whether they are new or returning."
},
"typeVersion": 1
},
{
"id": "9d052142-3a26-46b9-84dc-aba11307a616",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-832,
-128
],
"parameters": {
"color": 7,
"width": 1072,
"height": 304,
"content": "## Create candidate and parse resume\n\nCreates a new candidate record in Airtable, reattaches the resume binary, extracts text via LlamaParse, checks parsing quality, and branches based on success."
},
"typeVersion": 1
},
{
"id": "f4e63bab-3077-4799-9122-ec05fac8d542",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-832,
208
],
"parameters": {
"color": 7,
"height": 384,
"content": "## Flag duplicate candidate\n\nIf the candidate already exists, links the new submission to their existing Airtable record."
},
"typeVersion": 1
},
{
"id": "b255af59-a703-42bd-ae68-362cef67acb9",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
272,
64
],
"parameters": {
"color": 7,
"height": 336,
"content": "## Flag for manual review\n\nIf resume parsing fails quality checks, flags the candidate in Airtable for manual review."
},
"typeVersion": 1
},
{
"id": "461759e5-ed5d-4b50-80ea-d05126d969eb",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
272,
-464
],
"parameters": {
"color": 7,
"width": 1072,
"height": 496,
"content": "## AI screen candidate with rubric\n\nRetrieves the role's scoring rubric from Airtable, sanitizes the resume text, runs AI screening via DeepSeek, validates the JSON score output, and saves the result to Airtable."
},
"typeVersion": 1
},
{
"id": "dad46c4a-2eb5-4582-bfb7-d6bd4413e070",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
1376,
-224
],
"parameters": {
"color": 7,
"width": 752,
"height": 432,
"content": "## Human review and final decision\n\nSends the candidate to a human reviewer, then branches based on their decision to mark the candidate as either Advanced or Rejected in Airtable."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "cc5567ec-6dd9-4bd6-80cd-9bb2b6d14a42",
"connections": {
"Gmail Trigger": {
"main": [
[
{
"node": "Lookup Existing Candidate",
"type": "main",
"index": 0
}
]
]
},
"If Candidate Is New": {
"main": [
[
{
"node": "Create New Candidate Record",
"type": "main",
"index": 0
}
],
[
{
"node": "Link to Duplicate Record",
"type": "main",
"index": 0
}
]
]
},
"Send for Human Review": {
"main": [
[
{
"node": "Route by Human Decision",
"type": "main",
"index": 0
}
]
]
},
"Evaluate Parse Quality": {
"main": [
[
{
"node": "If Parse Quality Acceptable",
"type": "main",
"index": 0
}
]
]
},
"Route by Human Decision": {
"main": [
[
{
"node": "Update Record as Advanced",
"type": "main",
"index": 0
}
],
[
{
"node": "Update Record as Rejected",
"type": "main",
"index": 0
}
]
]
},
"DeepSeek Screening Model": {
"ai_languageModel": [
[
{
"node": "Screen Candidate with AI",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Screen Candidate with AI": {
"main": [
[
{
"node": "Validate AI Score Output",
"type": "main",
"index": 0
}
]
]
},
"Validate AI Score Output": {
"main": [
[
{
"node": "Save Screening Score to Airtable",
"type": "main",
"index": 0
}
]
]
},
"Lookup Existing Candidate": {
"main": [
[
{
"node": "If Candidate Is New",
"type": "main",
"index": 0
}
]
]
},
"Create New Candidate Record": {
"main": [
[
{
"node": "Reattach Resume Binary Data",
"type": "main",
"index": 0
}
]
]
},
"If Parse Quality Acceptable": {
"main": [
[
{
"node": "Fetch Role Rubric from Airtable",
"type": "main",
"index": 0
}
],
[
{
"node": "Flag Candidate for Manual Review",
"type": "main",
"index": 0
}
]
]
},
"Reattach Resume Binary Data": {
"main": [
[
{
"node": "Extract Resume Text via LlamaParse",
"type": "main",
"index": 0
}
]
]
},
"Sanitize and Clean Resume Text": {
"main": [
[
{
"node": "Screen Candidate with AI",
"type": "main",
"index": 0
}
]
]
},
"Fetch Role Rubric from Airtable": {
"main": [
[
{
"node": "Sanitize and Clean Resume Text",
"type": "main",
"index": 0
}
]
]
},
"Save Screening Score to Airtable": {
"main": [
[
{
"node": "Send for Human Review",
"type": "main",
"index": 0
}
]
]
},
"Extract Resume Text via LlamaParse": {
"main": [
[
{
"node": "Evaluate Parse Quality",
"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.
airtableTokenApideepSeekApigotoHumanApillamaParseApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow collects job applications via Gmail, deduplicates candidates in Airtable, extracts resume text with LlamaParse, scores candidates against an Airtable role rubric using a DeepSeek chat model, and routes results to gotoHuman for approve/reject decisions that update…
Source: https://n8n.io/workflows/17172/ — 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.
Email pdf analysis with Poppler. Uses gmailTrigger, readWriteFile, executeCommand, extractFromFile. Event-driven trigger; 8 nodes.
This n8n template automatically processes inbound form leads, researches their company via their website, and uses AI to write and send a highly personalized "speed to lead" email instantly. It also t
Process CV with AI. Uses gmailTrigger, gmail, splitInBatches, extractFromFile. Event-driven trigger; 48 nodes.
This workflow collects job applications via an n8n Form, checks duplicates in Airtable, stores CV files in Google Drive, and uses OpenRouter to score and summarize each CV for an n8n Automation Expert
Wait Splitout. Uses lmOpenAi, outputParserStructured, httpRequest, stickyNote. Event-driven trigger; 26 nodes.