This workflow corresponds to n8n.io template #15485 — we link there as the canonical source.
This workflow follows the Gmail Trigger → Google Sheets 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": "GgSrVbTbXd9TTR84",
"name": "Save SES project emails from Gmail to Google Sheets",
"tags": [],
"nodes": [
{
"id": "88f4ae9b-02bf-4030-a146-64afe883fe7e",
"name": "Gmail Trigger",
"type": "n8n-nodes-base.gmailTrigger",
"position": [
-304,
400
],
"parameters": {
"simple": false,
"filters": {},
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
}
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "79268d7d-1a19-4297-a56d-5078c0c3b7c9",
"name": "AI Project Classifier",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
16,
400
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini",
"cachedResultName": "GPT-4.1-MINI"
},
"options": {
"temperature": 0
},
"messages": {
"values": [
{
"role": "system",
"content": "You are a sales assistant at an SES (System Engineering Service) company.\nAnalyze the received email and determine whether it is a project recruitment email (engineer recruitment, job posting, or project introduction).\n\nCriteria for classifying as a project recruitment email:\n- Engineer recruitment or introduction\n- Development project introduction\n- Freelance / SES contract project\n- Content seeking specific skills or experience\n- Mentions of rates or compensation\n\nRespond ONLY in JSON format. No preamble or explanation needed.\n\nOutput format:\n{\n \"is_project\": true/false,\n \"project_name\": \"Project name (use 'Unknown' if unclear)\",\n \"category\": \"App / Infrastructure / Unknown (if determinable)\",\n \"details\": \"Project overview and details\",\n \"client_company\": \"Client company (use 'Unknown' if unclear)\",\n \"rate\": \"Rate (10k JPY units) (e.g. 70~80, use 'Unknown' if unclear)\",\n \"remote\": \"Remote available / No remote / Partial remote / Unknown\",\n \"location\": \"Work location (use 'Unknown' if unclear)\",\n \"working_hours\": \"Working hours (e.g. 09:00~18:00, use 'Unknown' if unclear)\",\n \"required_skills\": \"Required skills (comma-separated)\",\n \"preferred_skills\": \"Nice-to-have skills (comma-separated)\",\n \"notes\": \"Other notes\"\n}:"
},
{
"content": "=Analyze this email.\n\nSubject: {{ $json.headers.subject }}\nSender: {{ $json.headers.from }}\nBody:\n{{ $json.text }}"
}
]
}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "a241407f-bb04-47e4-8c4a-ce184ef61c74",
"name": "Parse JSON",
"type": "n8n-nodes-base.code",
"position": [
416,
400
],
"parameters": {
"jsCode": "const rawContent = $input.item.json.message.content;\n\nlet parsed;\ntry {\n // \u30b3\u30fc\u30c9\u30d6\u30ed\u30c3\u30af\u306e\u9664\u53bb\n const cleaned = rawContent\n .replace(/```json\\n?/g, '')\n .replace(/```\\n?/g, '')\n .trim();\n parsed = JSON.parse(cleaned);\n} catch (e) {\n return [{ json: { is_project: false, parse_error: e.message, raw: rawContent } }];\n}\n\nreturn [{ json: parsed }];"
},
"typeVersion": 2
},
{
"id": "fe384ebc-3cb2-417e-8105-9a848bc8ec5c",
"name": "Is Project Email?",
"type": "n8n-nodes-base.if",
"position": [
704,
400
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition-001",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.is_project }}",
"rightValue": true
}
]
}
},
"typeVersion": 2
},
{
"id": "cb2820b7-8785-49f4-9640-b8fe13fa2ea2",
"name": "Append to Spreadsheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
1536,
256
],
"parameters": {
"columns": {
"value": {
"rate": "={{ $('Parse JSON').item.json.rate }}",
"notes": "={{ $('Parse JSON').item.json.notes }}",
"remote": "={{ $('Parse JSON').item.json.remote }}",
"details": "={{ $('Parse JSON').item.json.details }}",
"category": "={{ $('Parse JSON').item.json.category }}",
"location": "={{ $('Parse JSON').item.json.location }}",
"project_name": "={{ $('Parse JSON').item.json.project_name }}",
"working_hours": "={{ $('Parse JSON').item.json.working_hours }}",
"client_company": "={{ $('Parse JSON').item.json.client_company }}",
"required_skills": "={{ $('Parse JSON').item.json.required_skills }}",
"preferred_skills": "={{ $('Parse JSON').item.json.preferred_skills }}"
},
"schema": [
{
"id": "project_name",
"type": "string",
"display": true,
"required": false,
"displayName": "project_name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "category",
"type": "string",
"display": true,
"required": false,
"displayName": "category",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "details",
"type": "string",
"display": true,
"required": false,
"displayName": "details",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "client_company",
"type": "string",
"display": true,
"required": false,
"displayName": "client_company",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "rate",
"type": "string",
"display": true,
"required": false,
"displayName": "rate",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "remote",
"type": "string",
"display": true,
"required": false,
"displayName": "remote",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "location",
"type": "string",
"display": true,
"required": false,
"displayName": "location",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "working_hours",
"type": "string",
"display": true,
"required": false,
"displayName": "working_hours",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "required_skills",
"type": "string",
"display": true,
"required": false,
"displayName": "required_skills",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "preferred_skills",
"type": "string",
"display": true,
"required": false,
"displayName": "preferred_skills",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "notes",
"type": "string",
"display": true,
"required": false,
"displayName": "notes",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Spreadsheet ID",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Spreadsheet ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Sheet Name",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Sheet Name",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $json['Sheet Name'] }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $json['Spreadsheet ID'] }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4
},
{
"id": "db85cab3-666d-4c9a-9f16-223abda077c9",
"name": "Skip (Non-Project Email)",
"type": "n8n-nodes-base.noOp",
"position": [
1040,
512
],
"parameters": {},
"typeVersion": 1
},
{
"id": "40b76b49-5622-4be3-b4b7-29d53aa96093",
"name": "Set Spreadsheet Config",
"type": "n8n-nodes-base.set",
"position": [
1040,
192
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "6772c355-6946-40fe-9b59-2516ba365dd6",
"name": "Spreadsheet ID",
"type": "string",
"value": ""
},
{
"id": "5d3adab8-acf2-4458-8825-e10e242055b3",
"name": "Sheet Name",
"type": "string",
"value": "Projects"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "659ba840-9cb1-44c9-bf35-e8f1de7b8379",
"name": "Workflow Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-400,
-160
],
"parameters": {
"width": 620,
"height": 492,
"content": "## SES Project Management \u2014 Gmail \u2192 Spreadsheet Auto-Entry\n\n**What this workflow does:**\nThis workflow monitors your Gmail inbox every minute and automatically extracts SES (System Engineering Service) project information from recruitment emails. It uses AI to classify emails and parse structured data, then appends project details to a Google Spreadsheet.\n\n**Workflow steps:**\n1. Gmail Trigger polls your inbox every minute\n2. AI Project Classifier analyzes each email to determine if it's a project recruitment email\n3. Parse JSON converts the AI response into structured data\n4. Is Project Email? routes project emails forward; others are skipped\n5. Set Spreadsheet Config sets the target spreadsheet ID and sheet name\n6. Append to Spreadsheet writes the extracted project data as a new row\n\n**Requirements:**\n- Gmail OAuth2 credentials\n- OpenAI API credentials\n- Google Sheets OAuth2 credentials\n- A Google Spreadsheet with a sheet named `Projects` (or update the sheet name in the Set Spreadsheet Config node)"
},
"typeVersion": 1
},
{
"id": "9c6c2020-05f7-4259-8c0e-06e92119a34c",
"name": "Note: Gmail Trigger",
"type": "n8n-nodes-base.stickyNote",
"position": [
-384,
576
],
"parameters": {
"color": 7,
"width": 260,
"height": 120,
"content": "**Gmail Trigger**\nPolls inbox every minute.\nAdjust the poll interval or add filters (e.g. label, sender) to reduce noise."
},
"typeVersion": 1
},
{
"id": "55bd0fc9-500d-46f0-96e1-b89bef448e0c",
"name": "Note: AI Classifier",
"type": "n8n-nodes-base.stickyNote",
"position": [
-16,
576
],
"parameters": {
"color": 7,
"width": 280,
"height": 168,
"content": "**AI Project Classifier**\nUses GPT-4.1-mini to read the email subject and body and return structured JSON indicating whether the email is a project recruitment post, along with extracted fields (rate, skills, location, etc.)."
},
"typeVersion": 1
},
{
"id": "b6d1227b-a711-4ffc-bdd7-ee63fbd24c17",
"name": "Note: Config",
"type": "n8n-nodes-base.stickyNote",
"position": [
960,
-16
],
"parameters": {
"color": 4,
"width": 360,
"height": 172,
"content": "**Set Spreadsheet Config**\n\u2699\ufe0f Configure here:\n- `Spreadsheet ID`: Replace with your Google Spreadsheet ID (found in the sheet URL)\n- `Sheet Name`: Name of the target sheet tab (default: `Projects`)"
},
"typeVersion": 1
},
{
"id": "8fbc012d-fec5-44fa-b8ea-b794c60b52fb",
"name": "Note: Spreadsheet",
"type": "n8n-nodes-base.stickyNote",
"position": [
1344,
96
],
"parameters": {
"color": 7,
"width": 508,
"height": 144,
"content": "**Append to Spreadsheet**\nWrites one row per project email with these columns:\n`project_name`, `category`, `details`, `client_company`, `rate`, `remote`, `location`, `working_hours`, `required_skills`, `preferred_skills`, `notes`\n\nMake sure your spreadsheet has these column headers in the first row."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "1fea2a20-0f33-4538-b478-01ab9f4c166d",
"connections": {
"Parse JSON": {
"main": [
[
{
"node": "Is Project Email?",
"type": "main",
"index": 0
}
]
]
},
"Gmail Trigger": {
"main": [
[
{
"node": "AI Project Classifier",
"type": "main",
"index": 0
}
]
]
},
"Is Project Email?": {
"main": [
[
{
"node": "Set Spreadsheet Config",
"type": "main",
"index": 0
}
],
[
{
"node": "Skip (Non-Project Email)",
"type": "main",
"index": 0
}
]
]
},
"AI Project Classifier": {
"main": [
[
{
"node": "Parse JSON",
"type": "main",
"index": 0
}
]
]
},
"Set Spreadsheet Config": {
"main": [
[
{
"node": "Append to Spreadsheet",
"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.
gmailOAuth2googleSheetsOAuth2ApiopenAiApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow monitors your Gmail inbox every minute and automatically extracts SES (System Engineering Service) project information from recruitment emails. It uses AI to classify emails and parse structured data, then appends project details to a Google Spreadsheet. Gmail…
Source: https://n8n.io/workflows/15485/ — 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.
Send A Chatgpt Email Reply And Save Responses To Google Sheets. Uses openAi, gmailTrigger, stickyNote, gmail. Event-driven trigger; 49 nodes.
Code. Uses openAi, gmailTrigger, stickyNote, gmail. Event-driven trigger; 49 nodes.
This workflow sends a OpenAI GPT reply when an email is received from specific email recipients. It then saves the initial email and the GPT response to an automatically generated Google spreadsheet.
n8n Recruitment. Uses gmailTrigger, openAi, googleSheets, gmail. Event-driven trigger; 20 nodes.
Email AI Agent. Uses gmailTrigger, textClassifier, lmChatOpenAi, gmail. Event-driven trigger; 18 nodes.