{
  "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
          }
        ]
      ]
    }
  }
}