{
  "nodes": [
    {
      "parameters": {
        "operation": "getAll",
        "limit": 200,
        "filters": {
          "q": "in:anywhere -in:trash -in:spam -label:AI/Disposable -label:AI/Archive -label:AI/Preserve -label:AI/Subscriptions -label:AI/Review"
        }
      },
      "id": "64222be2-63a6-471c-b1de-0e364c1d1c60",
      "name": "Gmail Search",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        96,
        -128
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const emails = $input.all();\n\nconst batch = emails.map((item, index) => {\n\n  const labels = item.json.labels || [];\n\n  const labelNames = labels.map(label =>\n    (label.name || label.id || '').toUpperCase()\n  );\n\n  const subject = item.json.Subject || '';\n  const from = item.json.From || '';\n\n  return {\n    index,\n\n    threadId: item.json.threadId,\n\n    from,\n\n    subject,\n\n    snippet: (item.json.snippet || '')\n      .substring(0, 300),\n\n    starred:\n      labelNames.includes('STARRED'),\n\n    hasReplyIndicator:\n      /^re:/i.test(subject) ||\n      /^fwd:/i.test(subject),\n\n    isPromotion:\n      labelNames.includes('CATEGORY_PROMOTIONS'),\n\n    isSpam:\n      labelNames.includes('SPAM')\n  };\n\n});\n\n\nreturn [\n  {\n    json: {\n      emails: batch\n    }\n  }\n];"
      },
      "id": "a5fde85c-5682-48f6-8db2-5149c8fa2ea8",
      "name": "Prepare Gemini Batch",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        688,
        -128
      ]
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "value": "models/gemini-3.1-flash-lite",
          "mode": "list",
          "cachedResultName": "models/gemini-3.1-flash-lite"
        },
        "messages": {
          "values": [
            {
              "content": "=Classify these emails.\n\nReturn EXACTLY this JSON array format:\n[\n  {\n    \"index\": 0,\n    \"category\": \"preserve\"\n  }\n]\n\nEmails:\n{{ JSON.stringify($json.emails) }}"
            }
          ]
        },
        "builtInTools": {},
        "options": {
          "systemMessage": "You are an email classification engine.\n\nYour task is to classify every email into exactly ONE category:\n\n- preserve\n- subscription\n- archive\n- disposable\n\n\nYou will receive:\n\n- from\n- subject\n- snippet\n- starred\n- hasReplyIndicator\n- isPromotion\n- isSpam\n\n\nDecision rules (highest priority first):\n\n\n1. STARRED EMAILS\n\nIf starred is true:\n\n\u2192 preserve\n\n\n2. HUMAN COMMUNICATION\n\nIf the email appears to be a real conversation between people:\n\n\u2192 preserve\n\nExamples:\n\n- personal emails\n- business discussions\n- negotiations\n- manually written replies\n- customer conversations\n- agreements\n\n\n3. SECURITY / LEGAL / CRITICAL\n\nPreserve emails containing:\n\n- password reset\n- authentication codes\n- MFA / 2FA\n- login alerts\n- security warnings\n- account compromise warnings\n- contracts\n- legal documents\n- government communication\n\n\n4. SUBSCRIPTIONS / RECURRING SERVICE PAYMENTS\n\nIf the email is a recurring service bill:\n\n\u2192 subscription\n\nExamples:\n\n- mobile phone bill\n- domain renewal, hosting, internet provider\n- streaming (Spotify, Netflix, YouTube Premium)\n- SaaS / software subscriptions\n- digital subscriptions\n- insurance premium\n- gym / membership fees\n- any recurring periodic service charge\n\n\n5. ARCHIVE\n\nArchive emails with future reference value:\n\nExamples:\n\n- product purchases / webshop orders / invoices for bought items\n- receipts\n- shipping / tracking\n- bookings / reservations\n- payment confirmations (non-subscription)\n- account information\n- work-related information\n\n\n6. DISPOSABLE\n\nDisposable emails are:\n\n- newsletters\n- marketing emails\n- advertisements\n- promotions\n- sales offers\n- product announcements\n- WordPress notifications\n- plugin updates\n- monitoring alerts\n- backup reports\n- logs\n- automated system notifications\n\n\nAdditional rules:\n\n- A starred email is ALWAYS preserve.\n- Human communication ALWAYS beats automated classification.\n- Do not delete anything.\n- Prefer archive over disposable if information may be useful later.\n- Promotional emails are usually disposable.\n- Category values must be lowercase and exactly one of: preserve, subscription, archive, disposable.\n\n\nOutput ONLY valid JSON.\n\nThe response MUST start with '[' and end with ']'.\n\nValid category values (lowercase, exactly one of): preserve, subscription, archive, disposable\n\n\nDo not include:\n- markdown\n- explanations\n- code fences\n- additional text",
          "maxOutputTokens": 8192,
          "temperature": 0.1,
          "topP": 0.1
        }
      },
      "id": "257792e6-6fe6-4f1a-b752-51a93eb1a6fd",
      "name": "Google Gemini",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "typeVersion": 1.2,
      "position": [
        1152,
        -128
      ],
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Parse Batch Result (n8n Code node, typeVersion 2)\n// Validates the Gemini JSON, applies the safety rules, and maps category -> Gmail Label ID.\n// Single responsibility: validate + map (rule 2.4 / rule 2.6).\n\nconst rawResponse =\n  $input.first().json.content?.parts?.[0]?.text ||\n  $input.first().json.text ||\n  '';\n\nlet results = [];\n\nconst cleaned = rawResponse\n  .replace(/```json/gi, '')\n  .replace(/```/g, '')\n  .trim();\n\n\ntry {\n\n  results = JSON.parse(cleaned);\n\n} catch(error) {\n\n  throw new Error(\n    'Gemini JSON parse error: ' +\n    error.message +\n    '\\nResponse:\\n' +\n    cleaned.substring(0,500)\n  );\n\n}\n\n\nconst labels = {\n\n  disposable:\n    'YOUR_LABEL_ID_DISPOSABLE',\n\n  archive:\n    'YOUR_LABEL_ID_ARCHIVE',\n\n  preserve:\n    'YOUR_LABEL_ID_PRESERVE',\n\n  subscription:\n    'YOUR_LABEL_ID_SUBSCRIPTIONS',\n\n  review:\n    'YOUR_LABEL_ID_REVIEW'\n\n};\n\n\nconst emails =\n  $('Prepare Gemini Batch')\n    .first()\n    .json\n    .emails;\n\n\nconst seenThreads = new Set();\n\nconst output = [];\n\n\n// Pass 1 \u2014 classify the emails that Gemini returned results for\nfor (const result of results) {\n\n\n  const email =\n    emails[result.index];\n\n\n  if (!email) {\n    continue;\n  }\n\n\n  // Thread dedup: one label per conversation (rule 2.5)\n  if (seenThreads.has(email.threadId)) {\n    continue;\n  }\n\n\n  seenThreads.add(email.threadId);\n\n\n  let category =\n    (result.category || '').toLowerCase().trim();\n\n\n  // Safety rule:\n  // a starred email is ALWAYS preserve (rule 2.2)\n\n  if (email.starred === true) {\n\n    category = 'preserve';\n\n  }\n\n\n  // Known spam -> disposable (unless starred overrode it)\n\n  if (email.isSpam && category !== 'preserve') {\n\n    category = 'disposable';\n\n  }\n\n\n  // Unknown AI category:\n  // not discarded \u2014 route to Review for manual decision\n\n  if (!labels[category]) {\n\n    category = 'review';\n\n  }\n\n\n  output.push({\n\n    json: {\n\n      threadId:\n        email.threadId,\n\n      category,\n\n      labelId:\n        labels[category]\n\n    }\n\n  });\n\n\n}\n\n\n// Pass 2 \u2014 emails that Gemini did NOT return a result for -> Review\nfor (const email of emails) {\n\n\n  if (seenThreads.has(email.threadId)) {\n    continue;\n  }\n\n\n  seenThreads.add(email.threadId);\n\n\n  let category = 'review';\n\n\n  if (email.starred === true) {\n\n    category = 'preserve';\n\n  }\n\n\n  if (email.isSpam && category !== 'preserve') {\n\n    category = 'disposable';\n\n  }\n\n\n  output.push({\n\n    json: {\n\n      threadId:\n        email.threadId,\n\n      category,\n\n      labelId:\n        labels[category]\n\n    }\n\n  });\n\n\n}\n\n\nreturn output;"
      },
      "id": "08532e3e-ff11-4a7a-aed1-fb16ea25e2de",
      "name": "Parse Batch Result",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1744,
        -128
      ]
    },
    {
      "parameters": {
        "resource": "thread",
        "operation": "addLabels",
        "threadId": "={{ $json.threadId }}",
        "labelIds": "={{ $json.labelId }}"
      },
      "id": "3de73050-2761-4ca8-818d-f2583dc4f150",
      "name": "Gmail Add Label",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        2288,
        -128
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "content": "# AI Gmail Classifier Workflow\n\n## Overview\n\nAI-powered Gmail organization workflow using Google Gemini.\n\nPurpose:\n- Analyze large Gmail inboxes\n- Automatically classify emails\n- Apply Gmail labels\n- Never delete emails\n\nCategories:\n\n\ud83d\udfe2 Preserve\nImportant emails that must be kept.\n\n\ud83d\udfe3 Subscriptions\nRecurring service bills and subscriptions.\n\n\ud83d\udfe1 Archive\nUseful information with future reference value.\n\n\ud83d\udd34 Disposable\nLow-value emails and inbox noise.\n\n\u26aa Review\nUnclear or unclassified emails \u2014 manual decision required.",
        "height": 724,
        "width": 406,
        "color": 7
      },
      "id": "0c6e43e2-89f5-49ca-8b4f-7f73f765ee63",
      "name": "DOC - Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -2144,
        48
      ]
    },
    {
      "parameters": {
        "content": "# Schedule Trigger\n\n## Purpose\n\nControls the automatic execution schedule of the AI Gmail Classification Workflow.\n\nThe workflow intentionally does not process the entire mailbox at once. It runs in controlled batches to allow:\n\n- gradual inbox cleanup\n- manual review of classifications\n- stable Gemini API usage\n- predictable execution time\n- lower risk of incorrect bulk actions\n\n---\n\n## Current Configuration\n\nTrigger Type:\nSchedule Trigger\n\nExecution Frequency:\n4 times per day\n\nSchedule:\n\n08:00\n12:00\n16:00\n20:00\n\nCron Expression:\n\n0 8,12,16,20 * * *\n\n---\n\n## Processing Strategy\n\nEach execution processes a maximum of:\n\n200 emails\n\nDaily maximum:\n\n4 executions \u00d7 200 emails = 800 emails/day\n\nThe workflow only processes emails that do not already have an AI classification label.\n\nExcluded labels:\n\n- AI/Disposable\n- AI/Archive\n- AI/Preserve\n- AI/Subscriptions\n- AI/Review\n\n---\n\n## Why Batch Processing?\n\nThe mailbox contains a large number of historical emails.\n\nProcessing everything at once would create unnecessary risks:\n\n- larger AI responses\n- increased JSON parsing failures\n- higher API usage spikes\n- harder troubleshooting\n- reduced manual control\n\nSmall controlled batches provide:\n\n- safer classification\n- easier validation\n- predictable costs\n\n---\n\n## Review Process\n\nAfter each execution:\n\n1. Check newly added Gmail labels.\n2. Review incorrectly classified emails.\n3. Adjust rules only if recurring classification issues appear.\n\nEmails the AI could not classify are routed to AI/Review for manual decision.\n\nThe workflow is designed for supervised cleanup, not blind automation.\n\n---\n\n## Future Optimization\n\nPossible future improvements:\n\n- dynamic batch sizing\n- retry handling\n- execution error notifications\n- scheduled frequency adjustment after initial cleanup\n\nCurrent priority:\nReliable classification and gradual mailbox organization.",
        "height": 640,
        "width": 420,
        "color": 5
      },
      "id": "a7bdb036-a95a-4543-8b7a-6310675a3a7e",
      "name": "DOC - Manual Trigger",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -528,
        48
      ]
    },
    {
      "parameters": {
        "content": "# Gmail Search\n\n## Purpose\n\nRetrieves emails that have not been processed yet.\n\nSearch filter:\n\nin:anywhere\n-in:trash\n-in:spam\n-label:AI/Disposable\n-label:AI/Archive\n-label:AI/Preserve\n-label:AI/Subscriptions\n-label:AI/Review\n\n## Protection\n\nAlready classified emails are ignored.\n\nTrash and Spam are excluded:\n- Trash threads cannot be labeled (Gmail rejects addLabels with \"Precondition check failed\" \u2014 error-008)\n- Spam is handled by Gmail itself and does not need AI labeling\n\nThis prevents:\n- Duplicate processing\n- Reclassification\n- Unnecessary Gemini API usage\n\nCurrent batch size:\n200 emails per execution.",
        "height": 670,
        "width": 450,
        "color": 5
      },
      "id": "a3c52bad-09a3-49bb-9bc3-e494b68dff2b",
      "name": "DOC - Gmail Search",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -32,
        48
      ]
    },
    {
      "parameters": {
        "content": "# Prepare Gemini Batch\n\n## Purpose\n\nTransforms Gmail API output into a compact AI-friendly format.\n\nThe node removes unnecessary Gmail metadata and keeps only classification-relevant information.\n\nData sent to Gemini:\n\n- index\n- threadId\n- from\n- subject\n- snippet (300 chars)\n- starred\n- hasReplyIndicator\n- isPromotion\n- isSpam\n\n## Optimization\n\nReducing payload size:\n\n- lowers token usage\n- lowers cost\n- improves classification focus\n\n## Star Handling\n\nGmail STARRED status is passed to Gemini and verified later in Parse Batch Result.",
        "height": 856,
        "width": 480,
        "color": 5
      },
      "id": "4c8017b9-d2cd-463f-8a72-aea7c9e0607b",
      "name": "DOC - Prepare Gemini Batch",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        512,
        48
      ]
    },
    {
      "parameters": {
        "content": "# Google Gemini Classification\n\n## Model\n\nGemini 3.1 Flash Lite\n\nSelected because the workflow requires:\n\n- Low cost\n- High throughput\n- Structured JSON output\n- Reliable classification\n\n## Output Format\n\nOnly JSON array is accepted:\n\n[\n  {\n    \"index\":0,\n    \"category\":\"preserve\"\n  }\n]\n\nNo:\n- Markdown\n- Explanation\n- Code blocks\n- Additional text",
        "height": 772,
        "width": 470,
        "color": 5
      },
      "id": "64c84a81-1fd0-4fc5-b00a-e3a798899c24",
      "name": "DOC - Gemini Classification",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1056,
        48
      ]
    },
    {
      "parameters": {
        "content": "# Gemini Classification Rules\n\n## Priority Order\n\n1. STARRED\n\nstarred=true\n\u2192 Preserve\n\n2. HUMAN COMMUNICATION\n\nReal conversations, replies, discussions.\n\u2192 Preserve\n\n3. SECURITY / LEGAL\n\nExamples:\n- Password reset\n- MFA codes\n- Security alerts\n- Contracts\n- Legal documents\n\n\u2192 Preserve\n\n4. SUBSCRIPTIONS\n\nRecurring service payments:\n\nExamples:\n- Mobile phone bills\n- Domain/hosting renewals\n- Internet provider\n- Streaming (Spotify, Netflix)\n- SaaS subscriptions\n- Insurance premiums\n- Gym memberships\n\n\u2192 Subscription\n\n5. ARCHIVE\n\nOne-time purchases and reference info:\n\nExamples:\n- Product purchases / webshop orders\n- Receipts\n- Shipping / tracking\n- Bookings / reservations\n- Payment confirmations\n- Account information\n\n\u2192 Archive\n\n6. DISPOSABLE\n\nExamples:\n- Marketing\n- Newsletters\n- Promotions\n- Logs\n- Monitoring alerts\n\n\u2192 Disposable",
        "height": 1080,
        "width": 480
      },
      "id": "9dd9b0a2-069f-49c8-8f2e-554b0b8b39ed",
      "name": "DOC - Gemini Rules",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1056,
        880
      ]
    },
    {
      "parameters": {
        "content": "# Parse Batch Result\n\n## Purpose\n\nProcesses Gemini response and prepares Gmail labeling.\n\nTwo-pass strategy:\n\nPass 1 \u2014 classify the emails Gemini returned results for.\nPass 2 \u2014 route unclassified emails (missing from the response) to Review.\n\nSteps:\n\n1. Extract Gemini response\n2. Remove formatting artifacts\n3. Parse JSON\n4. Validate category\n5. Map category to Gmail label\n6. Apply safety overrides\n7. Push unclassified emails -> Review\n\n## Safety Rules\n\nStarred emails:\n\nAlways forced to Preserve.\n\nSpam emails:\n\nForced to Disposable (unless starred).\n\nInvalid category:\n\nFallback \u2192 Review\n\nDuplicate threads:\n\nIgnored during the same execution.",
        "height": 760,
        "width": 480,
        "color": 5
      },
      "id": "63ee781f-b0e2-435d-af66-ae5eab29f5ef",
      "name": "DOC - Parse Batch Result",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1584,
        48
      ]
    },
    {
      "parameters": {
        "content": "# Gmail Label Mapping\n\nThe Parse Batch Result Code node maps each AI category to a Gmail Label ID.\n\nDisposable:\nYOUR_LABEL_ID_DISPOSABLE\n\nArchive:\nYOUR_LABEL_ID_ARCHIVE\n\nPreserve:\nYOUR_LABEL_ID_PRESERVE\n\nSubscriptions:\nYOUR_LABEL_ID_SUBSCRIPTIONS\n\nReview:\nYOUR_LABEL_ID_REVIEW\n\nLabel IDs are account-specific. Find yours with the Gmail API users.labels.list endpoint, then update the Parse Batch Result Code node.\nThe workflow only adds labels - no emails are deleted or modified.",
        "height": 524,
        "width": 414
      },
      "id": "05084410-5f7f-4dc5-b7dc-7c7230e56cd5",
      "name": "DOC - Gmail Labels",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2144,
        720
      ]
    },
    {
      "parameters": {
        "content": "# Gmail Add Label\n\n## Purpose\n\nApplies the selected AI category label to the Gmail thread.\n\nOperation:\n\nthread \u2192 addLabels\n\nInput:\n\nthreadId\nlabelId\n\nResult:\n\nThe email conversation receives the matching label:\n\nAI/Disposable\nAI/Archive\nAI/Preserve\nAI/Subscriptions\nAI/Review",
        "height": 620,
        "width": 430,
        "color": 5
      },
      "id": "0c6a297f-429a-4152-91e3-097fdcb8e884",
      "name": "DOC - Gmail Add Label",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2128,
        48
      ]
    },
    {
      "parameters": {
        "content": "# Performance & Cost Optimization\n\nCurrent configuration:\n\nBatch size:\n200 emails\n\nModel:\nGemini 3.1 Flash Lite\n\nOptimizations implemented:\n\n\u2713 Reduced Gmail payload\n\u2713 Removed unused AI fields\n\u2713 Removed confidence output\n\u2713 Removed reasoning output\n\u2713 Compact JSON response\n\u2713 Deterministic starred handling\n\nGoal:\nProcess large inboxes efficiently while keeping costs low.",
        "height": 596,
        "width": 450,
        "color": 5
      },
      "id": "04a10b4e-9a4d-4fb5-a772-4152d6fa68e9",
      "name": "DOC - Performance Optimization",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1696,
        48
      ]
    },
    {
      "parameters": {
        "content": "# Current Limitations\n\nNot implemented intentionally:\n\n- Database storage\n- Analytics layer\n- Logging system\n- Automatic retry logic\n- Pagination engine\n\nReason:\n\nKeep the workflow simple, transparent and maintainable.\n\nThe current objective is reliable inbox organization.",
        "height": 460,
        "width": 450,
        "color": 3
      },
      "id": "fb524e90-bed1-4cef-a36a-726532c6077e",
      "name": "DOC - Current Limitations",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1216,
        48
      ]
    },
    {
      "parameters": {
        "content": "# Workflow info\n\nThis is the single canonanical workflow file:\n- workflows/gmail-labeler.json\n\nIt supersedes the former versioned files (workflow_v1_4_subscriptions.json, workflow_v1_3_optimized.json). See workflows/README.md for version history.",
        "height": 864,
        "width": 1472
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2192,
        -32
      ],
      "typeVersion": 1,
      "id": "a04a2d6a-de87-4443-8372-58ce2def6acc",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8,12,16,20 * * *"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        -384,
        -128
      ],
      "id": "e86f52f4-c040-4892-b657-a96959ebf4b9",
      "name": "Schedule Trigger"
    },
    {
      "parameters": {
        "content": "# Setup & Credentials\n\n## n8n credentials to create\n\nGmail account:\nType: Google / Gmail OAuth2 API\n- Gmail read / send / modify checked\n- Scopes used: gmail.readonly (search), gmail.modify (add labels)\n- If connecting via Google Cloud Console: enable the Gmail API, create an OAuth2 Web application client, add the n8n OAuth redirect URI, request the two scopes above\n\nGoogle Gemini(PaLM) Api account:\nType: Google Gemini(PaLM) Api (API key)\n- API key from Google AI Studio (aistudio.google.com/app/apikey)\n- Enable the Generative Language API in Google Cloud\n\n## Required Gmail labels\n\nCreate these five labels in Gmail:\n- AI/Preserve\n- AI/Archive\n- AI/Disposable\n- AI/Subscriptions\n- AI/Review\n\nThen replace the placeholder label IDs in the Parse Batch Result Code node with your account's real Label IDs (see DOC - Gmail Labels).\nSecrets live only in n8n credentials - never in the workflow file."
      },
      "id": "3c290982-1366-4106-848f-3813812287ac",
      "name": "DOC - Setup & Credentials",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -2192,
        280
      ]
    }
  ],
  "connections": {
    "Gmail Search": {
      "main": [
        [
          {
            "node": "Prepare Gemini Batch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Gemini Batch": {
      "main": [
        [
          {
            "node": "Google Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini": {
      "main": [
        [
          {
            "node": "Parse Batch Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Batch Result": {
      "main": [
        [
          {
            "node": "Gmail Add Label",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Gmail Search",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {}
}