{
 "name": "RecipeAgents: Order emails to a Google Sheet with Gemini",
 "nodes": [
  {
   "parameters": {
    "pollTimes": {
     "item": [
      {
       "mode": "everyHour"
      }
     ]
    },
    "simple": false,
    "filters": {
     "q": "subject:(order OR receipt OR confirmation) -category:promotions"
    },
    "options": {}
   },
   "id": "b9c42f4f-89a2-48b4-a168-a167d72e44ba",
   "name": "Gmail Trigger",
   "type": "n8n-nodes-base.gmailTrigger",
   "typeVersion": 1.2,
   "position": [
    240,
    300
   ]
  },
  {
   "parameters": {
    "resource": "text",
    "operation": "message",
    "modelId": {
     "__rl": true,
     "value": "models/gemini-3.5-flash-lite",
     "mode": "id"
    },
    "messages": {
     "values": [
      {
       "content": "=Read this email. If it is not an order confirmation or a purchase receipt (for example a newsletter, an ad, or a shipping update), return {\"is_order\": false}. Otherwise return JSON: {\"is_order\": true, \"shop\", \"order_number\", \"order_date\" (YYYY-MM-DD), \"currency\" (3-letter code), \"total\" (number, exactly as printed), \"items\" (list of short names), \"delivery_estimate\", \"return_by\" (YYYY-MM-DD)}. Use null for anything the email doesn't state. Never calculate a total or a return date yourself. Return only the JSON. From: {{ $json.from.text }} Subject: {{ $json.subject }} Email: {{ ($json.text || '').slice(0, 20000) }}",
       "role": "user"
      }
     ]
    },
    "simplify": true,
    "jsonOutput": true,
    "options": {
     "includeMergedResponse": true
    }
   },
   "id": "4874db4b-21bf-4b1e-b1eb-43cb25b00878",
   "name": "Gemini: read the order",
   "type": "@n8n/n8n-nodes-langchain.googleGemini",
   "typeVersion": 1.1,
   "position": [
    500,
    300
   ]
  },
  {
   "parameters": {
    "jsCode": "// Turns the AI step's answer into one clean row per order email.\n// Reads Gemini (mergedResponse), Anthropic (merged_response) or OpenAI (message.content),\n// so you can swap the AI node without touching this one.\nconst rows = [];\nfor (const [i, item] of $input.all().entries()) {\n  const j = item.json;\n  const raw = j.mergedResponse ?? j.merged_response ?? j.message?.content ?? '';\n  let data;\n  try {\n    data = typeof raw === 'object' ? raw : JSON.parse(String(raw).replace(/^```(?:json)?\\s*|\\s*```$/g, '').trim());\n  } catch (e) {\n    data = { is_order: true, parse_error: String(raw).slice(0, 200) };\n  }\n  if (data.is_order === false) continue;\n  const mail = $('Gmail Trigger').itemMatching(i).json;\n  rows.push({ json: {\n    logged_at: $now.toISO(),\n    order_date: data.order_date ?? '',\n    shop: data.shop ?? '',\n    order_number: data.order_number ?? '',\n    currency: data.currency ?? '',\n    total: data.total ?? '',\n    items: Array.isArray(data.items) ? data.items.join('; ') : (data.items ?? ''),\n    delivery_estimate: data.delivery_estimate ?? '',\n    return_by: data.return_by ?? '',\n    from: mail.from?.text ?? '',\n    subject: mail.subject ?? '',\n    gmail_id: mail.id ?? '',\n    parse_error: data.parse_error ?? ''\n  }});\n}\nreturn rows;"
   },
   "id": "ffded106-0c1a-4455-abc6-6cb9286e5318",
   "name": "Parse JSON",
   "type": "n8n-nodes-base.code",
   "typeVersion": 2,
   "position": [
    760,
    300
   ]
  },
  {
   "parameters": {
    "operation": "append",
    "documentId": {
     "__rl": true,
     "value": "",
     "mode": "list"
    },
    "sheetName": {
     "__rl": true,
     "value": "",
     "mode": "list"
    },
    "columns": {
     "mappingMode": "autoMapInputData",
     "value": {}
    },
    "options": {}
   },
   "id": "9ea74e39-bb9a-4f2b-acfd-639c149c1287",
   "name": "Append to Sheet",
   "type": "n8n-nodes-base.googleSheets",
   "typeVersion": 4.5,
   "position": [
    1020,
    300
   ]
  },
  {
   "parameters": {
    "content": "## Order emails to a Google Sheet with Gemini\nFrom recipeagents.usestarbase.com/recipes/order-emails-to-sheet-gemini\n\n1. Connect credentials on each node (Gmail, Google Gemini(PaLM) API, Google Sheets).\n2. Pick your sheet/folder/channel and model.\n3. Run once with test data and check the output before activating.\nKeep a human check on anything customer-facing.",
    "height": 260,
    "width": 420
   },
   "id": "9d0368f0-c702-4fb5-beef-d2a8027b546a",
   "name": "Read me",
   "type": "n8n-nodes-base.stickyNote",
   "typeVersion": 1,
   "position": [
    200,
    20
   ]
  }
 ],
 "connections": {
  "Gmail Trigger": {
   "main": [
    [
     {
      "node": "Gemini: read the order",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Gemini: read the order": {
   "main": [
    [
     {
      "node": "Parse JSON",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Parse JSON": {
   "main": [
    [
     {
      "node": "Append to Sheet",
      "type": "main",
      "index": 0
     }
    ]
   ]
  }
 },
 "pinData": {},
 "active": false,
 "settings": {
  "executionOrder": "v1"
 },
 "meta": {
  "templateCredsSetupCompleted": false
 },
 "tags": []
}