{
  "id": "WIDvKmrZLeXJW2Nk",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Extract Purchase Orders from Gmail using Gemini AI and Save to Google Sheets",
  "tags": [],
  "nodes": [
    {
      "id": "be880ad2-61bc-4047-9ddf-a83d77e02e96",
      "name": "Get many messages",
      "type": "n8n-nodes-base.gmail",
      "position": [
        256,
        528
      ],
      "parameters": {
        "limit": 100,
        "simple": false,
        "filters": {
          "readStatus": "unread",
          "receivedAfter": "={{ $today.minus({ days: 1 }).toISODate() }}"
        },
        "options": {
          "downloadAttachments": true
        },
        "operation": "getAll"
      },
      "typeVersion": 2.1
    },
    {
      "id": "d992ac3e-171d-434b-a19c-457f43df78eb",
      "name": "Cron",
      "type": "n8n-nodes-base.cron",
      "position": [
        32,
        528
      ],
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "hour": 8
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "8bc15902-aaeb-4b13-b048-4ebab762a74d",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        704,
        528
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "60e07c88-8125-4a64-8212-14935fc3d73a",
              "operator": {
                "type": "object",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $('Filter emails').item.binary}}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ee93d6a2-74c9-4ba5-8efe-3246b62e85b7",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1152,
        544
      ],
      "parameters": {
        "text": "=# Role  \nYou are an Expert AI Agent specialized in reading emails, extracting purchase order details, enriching them with product information using the Google Sheet tool, and normalizing dates into calendar weeks (Kalenderwoche).  \n\n# Task  \n- Input: {{ $('If').item.json.text }}  \n- Context: The email may contain purchase order details either in:  \n  - A formatted table, or  \n  - A freeform paragraph.  \n- Language: The email text can be in any language. You must understand and process it.  \n\n# Instructions  \n1. Read the email text carefully.  \n2. Identify purchase order details, including:  \n   - Product/package name  \n   - Quantity / number of items  \n   - Any other order-related details (sizes, variants, etc. if available).  \n3. Detect **dates or time expressions** that refer to delivery, booking, or campaign execution.  \n   - If the text contains a direct week reference (e.g., *KW36*), use it directly.  \n   - If the text contains a month or vague time reference (e.g., *end of October*), convert it into the appropriate calendar week(s).  \n     - *\u201cStart of X month\u201d* \u2192 first calendar week of that month.  \n     - *\u201cMid of X month\u201d* \u2192 middle calendar week of that month.  \n     - *\u201cEnd of X month\u201d* \u2192 last calendar week of that month.  \n   - Use ISO week numbering (Monday as first day of the week).  \n4. Populate both `Kalenderwoche Start` and `Kalenderwoche Ende`.  \n   - If only one week is identified, set both Start and End to the same week.  \n   - If a date range is mentioned, map Start and End accordingly.  \n5. Query the **Google Sheet tool** to fetch full product details (e.g., product code, price, description, stock availability).  \n6. Merge the extracted order information with the Google Sheet product details.  \n7. **Return only the final `items` array as JSON. Do not include order_id, customer, or notes.**  \n8. Ensure all keys are translated into the email\u2019s language or standardized consistently.  \n\n# Output Format  \nReturn **only this JSON array**:  \n```json\n[\n  { \"Laufende Nummer\":\"<string>\",\n    \"Lieferant\":\"<string>\",\n    \"Lieferanten-Nr\": \"<number or null>\",\n    \"Marke\": \"<string or null>\",\n    \"Marken-Nr\": \"<number or null>\",\n    \"Kalenderwoche Start\": \"<string, e.g., KW36>\",\n    \"Kalenderwoche Ende\": \"<string, e.g., KW36>\",\n    \"Marketing Status\": \"<string or null>\",\n    \"Paket\": \"<string>\",\n    \"Produkt\": \"<string>\",\n    \"L\u00e4nder-Aktivierung\": \"<string or null>\",\n    \"Kosten\": \"<number or null>\",\n    \"quantity\": \"<number>\"\n  }\n]\n",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 2.2
    },
    {
      "id": "c2cbc323-d54a-436b-b4a9-443a21bed1eb",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        1152,
        768
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "cdff877c-5b0d-4286-988b-45633a1fd058",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "disabled": true,
      "position": [
        16,
        416
      ],
      "parameters": {
        "width": 608,
        "height": 320,
        "content": "## Scan Email on every minute and read new emails.\n**Get new emails frequently and filer them which has purchase order**"
      },
      "typeVersion": 1
    },
    {
      "id": "a3a9541a-a8d6-4f7c-b486-69d73c26346d",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        656,
        368
      ],
      "parameters": {
        "width": 400,
        "height": 368,
        "content": "## Check the document has attachment\n**Check for email without attachment. To read purchase order from the email body**"
      },
      "typeVersion": 1
    },
    {
      "id": "5c00dba7-eb59-4f61-b35e-69ef78f57360",
      "name": "Set final output keys",
      "type": "n8n-nodes-base.set",
      "position": [
        928,
        544
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "a05c046c-6afb-421d-8b9e-128d0960d006",
              "name": "final_json_keys",
              "type": "array",
              "value": "=[\n  \"Laufende Nummer\",\n  \"Lieferant\",\n  \"Lieferanten-Nr\",\n  \"Marke\",\n  \"Marken-Nr\",\n  \"Kalenderwoche\\nStart\",\n  \"Kalenderwoche\\nEnde\",\n  \"Marketing Status\",\n  \"Paket\",\n  \"Produkt\",\n  \"L\u00e4nder-Aktivierung\",\n  \"Kosten\"\n]\n"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "deb1c439-121b-42ad-b86b-e209ec852c58",
      "name": "Filter emails",
      "type": "n8n-nodes-base.filter",
      "position": [
        480,
        528
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "6b9ca560-7c08-40e0-9d9b-8a916ff1368c",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $json.subject }}",
              "rightValue": "=Marketing"
            },
            {
              "id": "518e65fe-c130-4a27-821b-20f6c51b2dd7",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $json.subject }}",
              "rightValue": "Buchungsanfrage"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "5c602373-6737-493e-b8a0-13d95ac6b4d5",
      "name": "Reformatted to upload in Google sheet",
      "type": "n8n-nodes-base.code",
      "position": [
        1504,
        544
      ],
      "parameters": {
        "jsCode": "let results = [];\n\nfor (const item of items) {\n  // Get the input string from each item\n  const input = item.json.output;\n\n  // Remove markdown ```json ... ``` wrappers if present\n  const cleaned = input.replace(/```json|```/g, '').trim();\n\n  // Only try parsing if the cleaned string starts with { or [\n  if (/^\\s*[\\{\\[]/.test(cleaned)) {\n    let parsed;\n    try {\n      parsed = JSON.parse(cleaned);\n    } catch (error) {\n      // Skip this item if parsing fails\n      continue;\n    }\n    // If parsed is an array, add each object as a separate item\n    if (Array.isArray(parsed)) {\n      for (const obj of parsed) {\n        results.push({ json: obj });\n      }\n    } else if (parsed && typeof parsed === 'object') {\n      results.push({ json: parsed });\n    }\n    // If parsed is empty or not an object, skip\n  }\n  // If not valid JSON, skip\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "1e737893-a942-40d0-b67f-92608c272cf9",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1088,
        400
      ],
      "parameters": {
        "width": 352,
        "height": 512,
        "content": "## AI Agent to read and summarize the order."
      },
      "typeVersion": 1
    },
    {
      "id": "ba711eee-2943-43c0-9b6f-b224fa663f58",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1472,
        432
      ],
      "parameters": {
        "width": 368,
        "height": 304,
        "content": "## Append purchase order to Google sheet\n"
      },
      "typeVersion": 1
    },
    {
      "id": "23258818-270c-47d3-8ebe-479359fd1e87",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -352,
        -192
      ],
      "parameters": {
        "width": 480,
        "height": 544,
        "content": "## \ud83d\udce7 Email Reading & Purchase Order Creation (AI-powered)\n\n**\u2728 What it does**  \n- \u23f1 Reads unread emails every minute  \n- \ud83c\udfaf Filters emails based on **Subject**  \n- \ud83e\udd16 Uses Gemini AI to summarize emails & extract purchase order details  \n- \ud83d\udcca Appends purchase order data to Google Sheets  \n\n**\ud83d\udee0 Requirements**  \n- \ud83d\udce9 Gmail account access to fetch unread emails  \n- \ud83d\udd11 Gemini AI credentials for summarization & extraction  \n- \ud83d\udcd1 Google Sheet with predefined purchase order headers  \n\n**\u2699\ufe0f Setup Instructions**  \n1. \ud83d\udd17 Set up Google Sheets & Gmail credentials in n8n  \n2. \ud83d\udcdd Configure the filter node with your subject rules  \n3. \ud83e\udd1d Connect Gemini AI with the correct credentials  \n4. \ud83d\udcc2 Create & configure a Google Sheet with the necessary purchase order headers  \n"
      },
      "typeVersion": 1
    },
    {
      "id": "c4f34423-862e-4165-be7f-8d79f6adb88d",
      "name": "Get row(s) in sheet in Google Sheets",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        1296,
        768
      ],
      "parameters": {
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "db30105a-0daf-4611-bebc-f86ad4db91bb",
      "name": "Append row in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1712,
        544
      ],
      "parameters": {
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.7
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "b7b5fd2c-fb75-42f6-9fc1-9a619d501c8b",
  "connections": {
    "If": {
      "main": [
        [],
        [
          {
            "node": "Set final output keys",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cron": {
      "main": [
        [
          {
            "node": "Get many messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Reformatted to upload in Google sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter emails": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get many messages": {
      "main": [
        [
          {
            "node": "Filter emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set final output keys": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet in Google Sheets": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Reformatted to upload in Google sheet": {
      "main": [
        [
          {
            "node": "Append row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}