{
  "name": "01 - Create Gmail Labels with GPT-5.6 Luna",
  "nodes": [
    {
      "parameters": {
        "content": "## 1. Analyze recent Gmail history and create useful labels\n\n**Manual setup stage.** Samples recent non-spam/non-trash Gmail messages, asks Replicate `openai/gpt-5.6-luna` with **no reasoning** for a small useful taxonomy, removes duplicates and every reserved/system label, then creates only missing user labels. It never removes labels, archives, deletes, or moves messages.",
        "height": 430,
        "width": 2670,
        "color": 4
      },
      "id": "s1-note",
      "name": "Stage 1 — Create Gmail labels",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -160,
        -720
      ]
    },
    {
      "parameters": {},
      "id": "s1-trigger",
      "name": "Create Gmail label taxonomy",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -80,
        -560
      ]
    },
    {
      "parameters": {
        "jsCode": "const recentMessageLimit = 100;\nconst searchQuery = 'in:anywhere -in:spam -in:trash';\nreturn [{ json: { recentMessageLimit, searchQuery } }];"
      },
      "id": "s1-settings",
      "name": "Gmail Label Analysis Settings",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        144,
        -560
      ]
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "resource": "message",
        "operation": "getAll",
        "returnAll": false,
        "limit": "={{ $json.recentMessageLimit }}",
        "simple": true,
        "filters": {
          "includeSpamTrash": false,
          "q": "={{ $json.searchQuery }}",
          "readStatus": "both"
        },
        "options": {}
      },
      "id": "s1-get-messages",
      "name": "Get Recent Gmail Messages",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        384,
        -560
      ]
    },
    {
      "parameters": {
        "jsCode": "const messages = $input.all().map(item => item.json);\nconst compact = messages.map(message => ({\n  from: message.From || message.from || '',\n  subject: message.Subject || message.subject || '(no subject)',\n  snippet: String(message.snippet || '').slice(0, 500),\n  existingLabels: (message.labels || []).map(label => label.name),\n}));\nconst prompt = [\n  'Design a practical Gmail user-label taxonomy from the email sample below.',\n  'Return ONLY valid JSON with this shape: {\"labels\":[{\"name\":\"Label name\",\"description\":\"What belongs here\"}]}.',\n  'Suggest 5 to 12 durable labels. Prefer broad, useful categories over sender-specific labels.',\n  'Gmail messages may keep multiple labels, but propose one concise taxonomy.',\n  'Never propose Gmail system/reserved labels or close variants: INBOX, SPAM, TRASH, UNREAD, STARRED, IMPORTANT, SENT, DRAFT, CHAT, ALL, ALL MAIL, CATEGORY_PERSONAL, CATEGORY_SOCIAL, CATEGORY_PROMOTIONS, CATEGORY_UPDATES, CATEGORY_FORUMS.',\n  'Do not propose actions that archive, delete, mark read, or remove labels.',\n  '',\n  JSON.stringify(compact),\n].join('\\n');\nreturn [{ json: { prompt, sampledMessageCount: compact.length } }];"
      },
      "id": "s1-build-prompt",
      "name": "Build Gmail Label Analysis Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        640,
        -560
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.replicate.com/v1/models/openai/gpt-5.6-luna/predictions",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Prefer",
              "value": "wait=60"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.replicateRequest }}",
        "options": {
          "timeout": 120000
        }
      },
      "id": "s1-ai",
      "name": "AI Suggests Gmail Labels",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        912,
        -560
      ]
    },
    {
      "parameters": {
        "jsCode": "function parseStructured(value) {\n  if (value && typeof value === 'object' && !Array.isArray(value)) {\n    if (value.json_output && typeof value.json_output === 'object') return value.json_output;\n    if (value.output?.json_output && typeof value.output.json_output === 'object') return value.output.json_output;\n    if (typeof value.text !== 'string' && typeof value.output?.text !== 'string') return value;\n  }\n  const source = typeof value?.text === 'string'\n    ? value.text\n    : typeof value?.output?.text === 'string'\n      ? value.output.text\n      : value;\n  const text = Array.isArray(source)\n    ? source.map(part => typeof part === 'string' ? part : JSON.stringify(part)).join('')\n    : String(source ?? '');\n  try { return JSON.parse(text); } catch {}\n  const match = text.match(/\\{[\\s\\S]*\\}/);\n  if (!match) return {};\n  try { return JSON.parse(match[0]); } catch { return {}; }\n}\nconst response = $input.first().json;\nconst parsed = parseStructured(response.output ?? response);\nconst labels = Array.isArray(parsed.labels) ? parsed.labels : [];\nreturn [{ json: { suggestions: labels.map(label => ({ name: String(label.name || '').trim(), description: String(label.description || '').trim() })) } }];"
      },
      "id": "s1-parse",
      "name": "Parse Suggested Gmail Labels",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1184,
        -560
      ]
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "resource": "label",
        "operation": "getAll",
        "returnAll": true
      },
      "id": "s1-get-labels",
      "name": "Get Existing Gmail Labels",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        1456,
        -560
      ]
    },
    {
      "parameters": {
        "jsCode": "const suggestions = $('Parse Suggested Gmail Labels').first().json.suggestions || [];\nconst existing = $input.all().map(item => item.json);\nconst existingNames = new Set(existing.map(label => String(label.name || '').trim().toLowerCase()));\nconst reserved = new Set(['inbox','spam','trash','unread','starred','important','sent','draft','chat','all','all mail','category_personal','category_social','category_promotions','category_updates','category_forums','personal','social','promotions','updates','forums']);\nconst seen = new Set();\nreturn suggestions\n  .filter(label => label.name && !reserved.has(label.name.toLowerCase()))\n  .filter(label => !existingNames.has(label.name.toLowerCase()))\n  .filter(label => !seen.has(label.name.toLowerCase()) && seen.add(label.name.toLowerCase()))\n  .map(label => ({ json: label }));"
      },
      "id": "s1-prepare",
      "name": "Prepare Missing User Labels",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1728,
        -560
      ]
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "resource": "label",
        "operation": "create",
        "name": "={{ $json.name }}",
        "options": {
          "labelListVisibility": "labelShow",
          "messageListVisibility": "show"
        }
      },
      "id": "s1-create",
      "name": "Create Gmail User Label",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        2000,
        -560
      ]
    },
    {
      "id": "s1-ai-request-builder",
      "name": "Build AI Suggests Gmail Labels Request",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        688,
        -560
      ],
      "parameters": {
        "jsCode": "return $input.all().map(item => {\n  const data = item.json;\n  const prompt = data.prompt;\n  return {\n    ...item,\n    json: {\n      ...data,\n      replicateRequest: {\n        input: {\n          prompt,\n          reasoning_effort: \"none\",\n          verbosity: 'low',\n          max_completion_tokens: 2000,\n        },\n      },\n    },\n  };\n});"
      }
    }
  ],
  "connections": {
    "Create Gmail label taxonomy": {
      "main": [
        [
          {
            "node": "Gmail Label Analysis Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail Label Analysis Settings": {
      "main": [
        [
          {
            "node": "Get Recent Gmail Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Recent Gmail Messages": {
      "main": [
        [
          {
            "node": "Build Gmail Label Analysis Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Gmail Label Analysis Prompt": {
      "main": [
        [
          {
            "node": "Build AI Suggests Gmail Labels Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Suggests Gmail Labels": {
      "main": [
        [
          {
            "node": "Parse Suggested Gmail Labels",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Suggested Gmail Labels": {
      "main": [
        [
          {
            "node": "Get Existing Gmail Labels",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Existing Gmail Labels": {
      "main": [
        [
          {
            "node": "Prepare Missing User Labels",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Missing User Labels": {
      "main": [
        [
          {
            "node": "Create Gmail User Label",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build AI Suggests Gmail Labels Request": {
      "main": [
        [
          {
            "node": "AI Suggests Gmail Labels",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": false,
    "timezone": "Australia/Sydney"
  },
  "versionId": "e311282d-34a0-4abe-b50b-61d5d1a67661",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
