This workflow corresponds to n8n.io template #8235 — we link there as the canonical source.
This workflow follows the Agent → HTTP Request 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 →
{
"meta": {
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "99132e0e-7083-49c2-822f-2cb10ac289b7",
"name": "Groq Chat Model4",
"type": "@n8n/n8n-nodes-langchain.lmChatGroq",
"position": [
3200,
-360
],
"parameters": {
"model": "moonshotai/kimi-k2-instruct",
"options": {
"temperature": 0.5
}
},
"credentials": {
"groqApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "09193e72-e51f-450d-bc0a-d6a8367f66ec",
"name": "Get profile details\u200b",
"type": "n8n-nodes-base.httpRequest",
"position": [
1780,
-700
],
"parameters": {
"url": "https://api.ghostgenius.fr/v2/profile",
"options": {},
"sendQuery": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"queryParameters": {
"parameters": [
{
"name": "url",
"value": "={{ $json.body.LinkedIn_CV }}"
}
]
}
},
"credentials": {
"httpBasicAuth": {
"name": "<your credential>"
},
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "c3ec14e5-245f-4624-890d-a958d1d05f86",
"name": "Get job details\u200b",
"type": "n8n-nodes-base.httpRequest",
"position": [
1780,
-460
],
"parameters": {
"url": "https://api.ghostgenius.fr/v2/job",
"options": {},
"sendQuery": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"queryParameters": {
"parameters": [
{
"name": "url",
"value": "={{ $json.body.LinkedIn_JD }}"
}
]
}
},
"credentials": {
"httpBasicAuth": {
"name": "<your credential>"
},
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "8c262246-2fc0-4d89-890f-aa7e7c3cd53f",
"name": "Webhook (GET Form details)",
"type": "n8n-nodes-base.webhook",
"position": [
940,
-560
],
"parameters": {
"path": "linkedin",
"options": {
"allowedOrigins": "*"
},
"httpMethod": "POST",
"responseMode": "responseNode",
"authentication": "basicAuth"
},
"credentials": {
"httpBasicAuth": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "760b8a86-745c-47c6-8bea-1a9f8ec11da1",
"name": "Build CV",
"type": "n8n-nodes-base.set",
"position": [
2060,
-700
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "622960b0-c560-43c1-acac-d690fe74c8e4",
"name": "headline",
"type": "string",
"value": "={{ $json.headline }}"
},
{
"id": "301956ad-6219-4d8a-9a00-e45cb4a8c941",
"name": "is_premium",
"type": "boolean",
"value": "={{ $json.is_premium }}"
},
{
"id": "01f8fa3c-75c6-4b57-9950-3d8a95fe1082",
"name": "is_creator",
"type": "boolean",
"value": "={{ $json.is_creator }}"
},
{
"id": "9a17e0b8-958e-40cf-ba05-665f1c5e418e",
"name": "geo",
"type": "object",
"value": "={{ $json.geo }}"
},
{
"id": "b77401d1-9ab1-41e2-870c-f8f43c4e141e",
"name": "is_hiring",
"type": "boolean",
"value": "={{ $json.is_hiring }}"
},
{
"id": "4a394eb9-8073-4e82-b65d-bd234ebec960",
"name": "summary",
"type": "string",
"value": "={{ $json.summary }}"
},
{
"id": "73f33e27-2803-43c3-ac79-c1f9c700bc54",
"name": "languages",
"type": "array",
"value": "={{ $json.languages }}"
},
{
"id": "f1628de0-e39a-4862-9732-7f65bb844216",
"name": "experiences",
"type": "array",
"value": "={{ $json.experiences }}"
},
{
"id": "6289a835-92b9-48a7-bce9-04abab217080",
"name": "skills",
"type": "array",
"value": "={{ $json.skills }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "cdba39fe-360b-4076-b1ba-295a7d8a7425",
"name": "Combine_CV",
"type": "n8n-nodes-base.aggregate",
"position": [
2340,
-660
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData",
"destinationFieldName": "CV"
},
"typeVersion": 1
},
{
"id": "1e7eb8b8-e895-4022-84a0-ce23cdfee286",
"name": "Build JD",
"type": "n8n-nodes-base.set",
"position": [
2040,
-460
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "8f021bed-f1bb-44ec-b9ec-f5a69f30e25d",
"name": "title",
"type": "string",
"value": "={{ $json.title }}"
},
{
"id": "7438183a-4fac-4b3a-b33a-13872c23bc4b",
"name": "description",
"type": "string",
"value": "={{ $json.description }}"
},
{
"id": "db24b36e-3e74-408f-87f2-f8c2af088167",
"name": "work_remote_allowed",
"type": "boolean",
"value": "={{ $json.work_remote_allowed }}"
},
{
"id": "0f954876-3608-46c3-85ac-c1808b04e056",
"name": "work_place",
"type": "string",
"value": "={{ $json.work_place }}"
},
{
"id": "3c32fe75-1777-49c7-b7c7-003f8a788644",
"name": "listed_at_date",
"type": "string",
"value": "={{ $json.listed_at_date }}"
},
{
"id": "28fc6507-acd1-4e85-a636-4d3b0a6a81e4",
"name": "contract_type",
"type": "string",
"value": "={{ $json.contract_type }}"
},
{
"id": "79d00697-99df-4503-8029-c65c52109cd4",
"name": "company",
"type": "object",
"value": "={{ $json.company }}"
},
{
"id": "374fba18-00d0-4cef-a7dc-018aad986754",
"name": "apply_method.company_apply_url",
"type": "string",
"value": "={{ $json.apply_method.company_apply_url }}"
},
{
"id": "fc5a8073-ea1f-4b36-94bb-6bab1f9203fd",
"name": "hiringTeam",
"type": "string",
"value": "={{ $json.hiringTeam }}"
},
{
"id": "0f3a24dc-7a06-47ef-be43-3aa7b8293609",
"name": "location",
"type": "string",
"value": "={{ $json.location }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "fdeb23f6-f2d7-42cf-b582-2a8dea61d370",
"name": "Combine_JD",
"type": "n8n-nodes-base.aggregate",
"position": [
2340,
-500
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData",
"destinationFieldName": "JD"
},
"typeVersion": 1
},
{
"id": "f4968f34-f4c4-4480-85bc-52c5d60dfe55",
"name": "ATS compare",
"type": "n8n-nodes-base.set",
"position": [
2940,
-600
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "eb3cb6e0-8bac-476a-ae4f-e76c87a1cf6f",
"name": "CV",
"type": "array",
"value": "={{ $json.CV }}"
},
{
"id": "84f7795e-ba27-4982-b6b4-f40676846153",
"name": "JD",
"type": "array",
"value": "={{ $json.JD }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "4cba76a8-a973-4450-8b82-34ccde324f96",
"name": "Merge2",
"type": "n8n-nodes-base.merge",
"position": [
2660,
-600
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineAll"
},
"typeVersion": 3
},
{
"id": "bb684bd3-d9a1-4f01-acd7-a82d977c08ad",
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
3980,
-620
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ $json }}"
},
"typeVersion": 1.1
},
{
"id": "56cde4ed-dcaa-4b8b-8432-77304a208867",
"name": "Recruiter Check",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
3260,
-620
],
"parameters": {
"text": "=Job Description JSON:\n{{ $json.JD.toJsonString()}}\n\nCandidate CV JSON:\n{{ $json.CV.toJsonString()}}\n---\n\nOutput ONLY one JSON object that conforms exactly to the Match_Result schema. All arrays must be included, even if empty. All string values must be in double quotes.",
"options": {
"systemMessage": "=You are an expert ATS evaluator, recruitment assistant, and career advisor. \nYour ONLY task is to analyze the given Job Description (JD) JSON and Candidate CV JSON, \ncompare them, and output a structured JSON matching the `Match_Result` schema below.\n\nImportant Output Rules:\n- Return ONLY valid JSON (nothing else, no prose, no markdown).\n- Always include ALL fields in the schema. If no matches, return an empty array for that field.\n- Use canonicalized keyword tokens for \"matched_keywords\", \"missing_keywords_required\", and \"missing_keywords_nice_to_have\".\n- When a candidate\u2019s CV provides evidence for a skill or requirement but phrases it differently, return the canonical keyword **followed by parentheses** with the CV\u2019s actual wording.\n - Example: `\"Quota carrying experience (Achieved 120% of sales targets as SDR)\"`.\n- Reasoning should be factual ATS\u2011style notes (explain what is matched/missing).\n- Recommendation = one clear action statement.\n- Optimization tips must be **CV-focused, practical actions** the candidate can take.\n\n---\n\nSchema (must always be returned exactly like this, with all keys):\n\n{\n \"status\": \"core_match | good_match | mismatch\",\n \"reasoning\": [\"string\"],\n \"recommendation\": \"string\",\n \"matched_keywords\": [\"string\"],\n \"missing_keywords_required\": [\"string\"],\n \"missing_keywords_nice_to_have\": [\"string\"],\n \"optimization_tips\": [\"string\"],\n \"location_match\" : [\"boolean\"]\n}\n\n---\n\nDefinitions:\n- \"mismatch\": Candidate fails at least 1 bare minimum requirement from JD (e.g., location, required language, quota-carrying).\n- \"core_match\": Candidate meets all minimums but does not exceed them.\n- \"good_match\": Candidate meets all minimums and shows extra strengths (additional experience, languages, certifications, strong metrics).\n\n---"
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.7
},
{
"id": "64629a52-6d90-4068-879b-268001080e7f",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
1180,
-560
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "4c3cb62d-3070-4105-8155-d05a08667548",
"operator": {
"type": "string",
"operation": "contains"
},
"leftValue": "={{ $json.body.LinkedIn_CV }}",
"rightValue": "linkedin.com/"
},
{
"id": "ff692e0f-21eb-45fa-96e7-3171588f5e90",
"operator": {
"type": "string",
"operation": "contains"
},
"leftValue": "={{ $json.body.LinkedIn_JD }}",
"rightValue": "linkedin.com/"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "25678030-b9a8-461a-80cb-d26ebd19cc34",
"name": "Error_node",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1560,
-280
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"thankYouMessage\": \"<div class='flex flex-col items-center justify-center text-center space-y-4 p-4'><h2 class='text-xl font-bold text-red-600'>Invalid URL</h2><p>The URL you submitted appears to be incorrect or inaccessible.</p><p>Please double-check the link and try again.</p></div>\"\n}"
},
"typeVersion": 1.1
},
{
"id": "d02f55f3-adf4-4219-8b13-4b40d46ea773",
"name": "ThankYOU message",
"type": "n8n-nodes-base.code",
"position": [
3660,
-600
],
"parameters": {
"jsCode": "return items.map(item => {\n let parsed = {};\n\n try {\n // Parse the \"output\" field which is a JSON string\n parsed = JSON.parse(item.json.output);\n } catch (error) {\n parsed = { error: 'Invalid JSON in output field', details: error.message };\n }\n\n function makeList(arr) {\n if (!arr || arr.length === 0) return \"<li>None</li>\";\n return arr.map(x => `<li>${x}</li>`).join(\"\");\n }\n\n const html = `\n <div class=\"flex flex-col items-start justify-start text-left space-y-4 p-4\">\n <h2 class=\"text-xl font-bold\">Analysis Result</h2>\n <p><strong>Status:</strong> ${parsed.status ?? ''}</p>\n <div class=\"text-sm space-y-2\">\n <p><strong>Reasoning:</strong></p>\n <ul class=\"list-disc list-inside text-left\">${makeList(parsed.reasoning)}</ul>\n </div>\n <p><strong>Recommendation:</strong> ${parsed.recommendation ?? ''}</p>\n <div class=\"text-sm space-y-2\">\n <p><strong>Matched Keywords:</strong></p>\n <ul class=\"list-disc list-inside text-left\">${makeList(parsed.matched_keywords)}</ul>\n </div>\n <div class=\"text-sm space-y-2\">\n <p><strong>Missing (Required):</strong></p>\n <ul class=\"list-disc list-inside text-left\">${makeList(parsed.missing_keywords_required)}</ul>\n </div>\n <div class=\"text-sm space-y-2\">\n <p><strong>Missing (Nice to have):</strong></p>\n <ul class=\"list-disc list-inside text-left\">${makeList(parsed.missing_keywords_nice_to_have)}</ul>\n </div>\n <div class=\"text-sm space-y-2\">\n <p><strong>Optimization Tips:</strong></p>\n <ul class=\"list-disc list-inside text-left\">${makeList(parsed.optimization_tips)}</ul>\n </div>\n </div>`;\n\n return {\n json: {\n thankYouMessage: html\n }\n };\n});"
},
"typeVersion": 2
},
{
"id": "d8f8bbeb-3199-43e2-8375-846d657b5ba5",
"name": "Workflow Description (Sticky Note)1",
"type": "n8n-nodes-base.stickyNote",
"position": [
420,
-1160
],
"parameters": {
"width": 440,
"height": 920,
"content": "## \ud83d\udccc Workflow Description\n\nThis workflow automates **CV vs JD matching** using LinkedIn profile data, job descriptions, and an AI recruiter check. It evaluates a candidate\u2019s CV against a job posting, highlights strengths and gaps, and provides an ATS-style analysis report.\n\n### \u2705 Who\u2019s it for\nRecruiters, hiring managers, and job seekers who want an **AI-powered Applicant Tracking System (ATS)** analysis to quickly evaluate the fit between a CV and a job description.\n\n### \u2699\ufe0f How it works\n1. The **Webhook** captures the LinkedIn CV and JD URLs.\n2. Two **HTTP Request nodes** fetch profile and job description details.\n3. The workflow **builds structured CV and JD JSON objects** using Set + Aggregate nodes.\n4. A **Merge + ATS Compare** step aligns the CV and JD.\n5. The **Recruiter Check (LLM)** analyzes the alignment using a defined schema.\n6. The **Code node** cleans this JSON, formats it, and generates the ready-to-render HTML summary.\n7. Finally, the **Respond to Webhook** node sends the result back.\n\n### \ud83d\udd27 Requirements\n* A valid LinkedIn CV/JD scraper API (via GhostGenius here).\n* A Groq account for the LLM step (no keys are hardcoded).\n* An n8n instance (cloud or self-hosted).\n\n### \ud83c\udfa8 How to Customize\n* Adjust the LLM prompt for more specific ATS scoring rules.\n* Change the Code node template to reformat results in plain text, Slack blocks, or PDF.\n* Add integrations (Slack, Gmail, Notion) to automatically distribute candidate/job match reports.\n\n\u26a0\ufe0f Credential Reminder: Do not hardcode sensitive API keys in nodes\u2014always store them securely in n8n credentials."
},
"typeVersion": 1
},
{
"id": "2dc1eb5a-c00f-4039-a317-8cea1880aa68",
"name": "Analysis Result (Sticky Note)",
"type": "n8n-nodes-base.stickyNote",
"position": [
4240,
-1080
],
"parameters": {
"width": 920,
"height": 940,
"content": "## \ud83d\udcdd Analysis Result\n\n**Status:** mismatch\n\n### Reasoning\n- Candidate location is United States; JD explicitly requires residence in the Netherlands.\n- JD mandates Dutch language fluency; languages array is empty in CV.\n- JD asks for 3-5 years closing sales experience; CV shows 12+ years (exceeds upper bound but not disqualifying).\n\n**Recommendation:** Do not proceed with application until candidate can relocate to the Netherlands and demonstrate Dutch fluency.\n\n### \u2705 Matched Keywords\n- Quota carrying experience (10x President\u2019s Club)\n- Closing Sales experience (Account Executive III)\n- Sales experience (Enterprise Account Executive)\n- Consultative selling (Growth Consultant @ HubSpot)\n- Pipeline management (accurate forecasting implied by President\u2019s Club)\n- High performer (10x President\u2019s Club)\n\n### \u274c Missing (Required)\n- Dutch fluency\n- Netherlands residence\n\n### \u26a0\ufe0f Missing (Nice to have)\n- Mid-market sales experience\n- Inside sales model\n- Inbound selling strategies\n- SMB focus\n\n### \ud83d\udca1 Optimization Tips\n- Add Dutch language proficiency and CEFR level in the Languages section.\n- Change geo location to Amsterdam, Netherlands.\n- Insert a bullet under each sales role specifying inbound/inside sales and mid-market/SMB focus.\n- Include quantified mid-market quota achievements (ARR, number of deals)."
},
"typeVersion": 1
}
],
"connections": {
"If": {
"main": [
[
{
"node": "Get profile details\u200b",
"type": "main",
"index": 0
},
{
"node": "Get job details\u200b",
"type": "main",
"index": 0
}
],
[
{
"node": "Error_node",
"type": "main",
"index": 0
}
]
]
},
"Merge2": {
"main": [
[
{
"node": "ATS compare",
"type": "main",
"index": 0
}
]
]
},
"Build CV": {
"main": [
[
{
"node": "Combine_CV",
"type": "main",
"index": 0
}
]
]
},
"Build JD": {
"main": [
[
{
"node": "Combine_JD",
"type": "main",
"index": 0
}
]
]
},
"Combine_CV": {
"main": [
[
{
"node": "Merge2",
"type": "main",
"index": 0
}
]
]
},
"Combine_JD": {
"main": [
[
{
"node": "Merge2",
"type": "main",
"index": 1
}
]
]
},
"Error_node": {
"main": [
[]
]
},
"ATS compare": {
"main": [
[
{
"node": "Recruiter Check",
"type": "main",
"index": 0
}
]
]
},
"Recruiter Check": {
"main": [
[
{
"node": "ThankYOU message",
"type": "main",
"index": 0
}
]
]
},
"Groq Chat Model4": {
"ai_languageModel": [
[
{
"node": "Recruiter Check",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"ThankYOU message": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Get job details\u200b": {
"main": [
[
{
"node": "Build JD",
"type": "main",
"index": 0
}
]
]
},
"Get profile details\u200b": {
"main": [
[
{
"node": "Build CV",
"type": "main",
"index": 0
}
]
]
},
"Webhook (GET Form details)": {
"main": [
[
{
"node": "If",
"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.
groqApihttpBasicAuthhttpHeaderAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Recruiter Mirror is a proof‑of‑concept ATS analysis tool for SDRs/BDRs. Compare your LinkedIn or CV to job descriptions and get recruiter‑ready insights.
Source: https://n8n.io/workflows/8235/ — 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.
Hi! I'm Bruno — I’ve been building AI-powered workflows for n8n and Make for 2+ years, focused on smart automation and real conversational agents.
This workflow contains community nodes that are only compatible with the self-hosted version of n8n. User Uploads PDF : The workflow accepts a PDF via webhook. Extract Text : n8n extracts the text con
This workflow monitors user usage via a webhook and automatically triggers an upsell process when limits are exceeded. It formats incoming data, generates a personalized email using AI, and sends it t
⏺ 🚀 How it works
L&D_AgentsAI_ATIVO. Uses httpRequest, agent, googleCalendarTool, toolSerpApi. Webhook trigger; 93 nodes.