{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "5e032005-5a5e-4b83-9ab0-b8d87d7e19ef",
      "name": "Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        11568,
        6096
      ],
      "parameters": {
        "width": 540,
        "height": 588,
        "content": "## n8n Template Approval Checker\n\nCheck your n8n workflow template against the latest official Creator Hub guidelines before submitting. This workflow fetches live approval criteria from Notion, generates a structured checklist with AI, then uploads your full workflow JSON to Gemini for a detailed pass/fail review. Results are sent to your email as an HTML report.\n\n### How it works\n1. You upload your workflow JSON file and enter the template title via the web form.\n2. The workflow scrapes four official n8n Creator Hub Notion pages using the Splitbee API.\n3. Gemini AI reads the scraped guidelines and generates a structured approval checklist.\n4. Both the checklist and your workflow JSON are uploaded to Gemini Files API.\n5. A second Gemini call reviews your workflow against every criterion and scores it.\n6. The HTML review report is emailed to the address you provided.\n\n### Setup steps\n1. Add your **Google Gemini API** credential (used for criteria generation, file upload, and review).\n2. Add your **Gmail OAuth2** credential (used to send the results email).\n3. Activate the workflow and open the form URL to start checking templates."
      },
      "typeVersion": 1
    },
    {
      "id": "c5d39394-175a-4893-b9cc-9a9e15933ddb",
      "name": "Step 1 Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        12128,
        6096
      ],
      "parameters": {
        "color": 7,
        "width": 228,
        "height": 320,
        "content": "## Form input\nCollects the workflow JSON file, template title, and email address for results delivery."
      },
      "typeVersion": 1
    },
    {
      "id": "d17599ce-329c-486e-bf4a-0aab16c86a65",
      "name": "Step 2 Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        12368,
        6096
      ],
      "parameters": {
        "color": 7,
        "width": 732,
        "height": 320,
        "content": "## Scrape guidelines\nFetches four Creator Hub pages via the Notion unofficial API, then extracts readable text from each block."
      },
      "typeVersion": 1
    },
    {
      "id": "c6937ce6-fd63-4cfa-b0d1-f2b82bb2c77e",
      "name": "Step 3 Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        13120,
        6096
      ],
      "parameters": {
        "color": 7,
        "width": 360,
        "height": 496,
        "content": "## AI criteria generation\nGemini reads the scraped guidelines and outputs a numbered pass/fail checklist."
      },
      "typeVersion": 1
    },
    {
      "id": "ae935fec-2b4f-40f2-94f6-a4b73689b766",
      "name": "Step 4 Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        13504,
        6096
      ],
      "parameters": {
        "color": 7,
        "width": 760,
        "height": 320,
        "content": "## Upload and review\nBoth files are uploaded to Gemini Files API, then a second Gemini call reviews the workflow against the checklist."
      },
      "typeVersion": 1
    },
    {
      "id": "34c5c85b-f1ec-4c43-8cbe-3739a93f7a7a",
      "name": "Step 5 Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        14272,
        6096
      ],
      "parameters": {
        "color": 7,
        "width": 310,
        "height": 320,
        "content": "## Email results\nThe HTML review report is sent to the email address provided in the form."
      },
      "typeVersion": 1
    },
    {
      "id": "5c8d63e9-ccbe-4e27-8e62-256d08de3017",
      "name": "Submit workflow for review",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        12192,
        6256
      ],
      "parameters": {
        "path": "169886ac-41b6-4986-97f2-2322d5506d7c",
        "options": {},
        "formTitle": "n8n Template Approval Checker",
        "formFields": {
          "values": [
            {
              "fieldType": "file",
              "fieldLabel": "Workflow JSON File",
              "requiredField": true
            },
            {
              "fieldLabel": "Template Title",
              "placeholder": "Workflow title (max 80 characters)",
              "requiredField": true
            },
            {
              "fieldLabel": "Email Address",
              "placeholder": "Email address for results delivery",
              "requiredField": true
            }
          ]
        },
        "formDescription": "Upload your workflow JSON to check it against the latest n8n Creator Hub approval guidelines."
      },
      "typeVersion": 2
    },
    {
      "id": "efebc416-a774-493a-be20-91b5dd23a6b0",
      "name": "Generate Notion API URLs",
      "type": "n8n-nodes-base.code",
      "position": [
        12416,
        6256
      ],
      "parameters": {
        "jsCode": "// Generate Notion API URLs for all guideline pages\nvar pageIds = [\n  '7bd2cbe0fce0449198ecb23ff4a2f76f',\n  '9959894476734da3b402c90b124b1f77',\n  '2aa5b6e0c94f8058b0aefddd02655887',\n  'b4c008b47eb74846b48c37c652ec2650'\n];\n\nreturn pageIds.map(function(id) {\n  return { json: { url: 'https://notion-api.splitbee.io/v1/page/' + id, pageId: id } };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "0ee6f803-37ab-4910-987f-b2d1c50bdd4c",
      "name": "Fetch guideline pages from Notion",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        12592,
        6256
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "05228ac8-d64b-4f20-84ec-7cf663d79509",
      "name": "Combine all fetched pages",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        12768,
        6256
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "a7702dec-5ae0-4ae2-99c4-a2e63922ad98",
      "name": "Extract text from Notion blocks",
      "type": "n8n-nodes-base.code",
      "position": [
        12944,
        6256
      ],
      "parameters": {
        "jsCode": "// Parse Notion API response into readable guidelines text\nfunction extractBlockText(block) {\n  if (!block || !block.value || !block.value.properties || !block.value.properties.title) return '';\n  var titleArr = block.value.properties.title;\n  var text = '';\n  for (var i = 0; i < titleArr.length; i++) {\n    if (typeof titleArr[i][0] === 'string') {\n      text = text + titleArr[i][0];\n    }\n  }\n  return text.trim();\n}\n\nfunction formatBlock(block) {\n  var text = extractBlockText(block);\n  if (!text || text === '\\u2023') return '';\n  var type = (block && block.value && block.value.type) ? block.value.type : '';\n  if (type === 'header') return '\\n# ' + text;\n  if (type === 'sub_header') return '\\n## ' + text;\n  if (type === 'sub_sub_header') return '\\n### ' + text;\n  if (type === 'bulleted_list') return '- ' + text;\n  if (type === 'numbered_list') return '1. ' + text;\n  if (type === 'callout') return '> ' + text;\n  if (type === 'toggle') return '> ' + text;\n  if (type === 'page') return '\\n--- PAGE: ' + text + ' ---';\n  return text;\n}\n\nvar guidelines = '';\nvar items = $input.all();\nfor (var idx = 0; idx < items.length; idx++) {\n  var itemData = items[idx].json;\n  if (!itemData) continue;\n  // Handle aggregated data (array of page results)\n  var pages = Array.isArray(itemData.data) ? itemData.data : [itemData];\n  for (var p = 0; p < pages.length; p++) {\n    var pageData = pages[p];\n    if (!pageData || typeof pageData !== 'object') continue;\n    var keys = Object.keys(pageData);\n    for (var k = 0; k < keys.length; k++) {\n      var block = pageData[keys[k]];\n      if (!block || !block.value) continue;\n      var formatted = formatBlock(block);\n      if (formatted) {\n        guidelines = guidelines + formatted + '\\n';\n      }\n    }\n  }\n}\n\n// Fallback if scraping failed\nif (!guidelines || guidelines.length < 100) {\n  guidelines = 'SCRAPING FAILED - Please check Notion API availability';\n}\n\nreturn {\n  json: {\n    guidelines: guidelines,\n    guidelinesLength: guidelines.length,\n    scrapedFromNotion: guidelines.length > 200,\n    source: guidelines.length > 200 ? 'Notion API (Live)' : 'Scraping Failed'\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "1959750c-ff89-461b-8841-0fa1b9fef52c",
      "name": "Generate approval checklist with AI",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        13184,
        6256
      ],
      "parameters": {
        "text": "=You are an n8n template marketplace expert.\n\nBelow are the OFFICIAL guidelines scraped from n8n's Creator Hub Notion pages:\n\n{{ $('Extract text from Notion blocks').first().json.guidelines.substring(0, 6000) }}\n\nBased on these official guidelines, create a STRUCTURED CHECKLIST of approval criteria.\nFormat it as a numbered list with clear pass/fail conditions.\n\nInclude criteria for:\n1. Node count requirements\n2. Integration/service requirements\n3. Sticky note requirements (colors, content, placement)\n4. Title format rules\n5. Description requirements\n6. Technical metadata (executionOrder, credentials, etc.)\n7. Connection completeness\n8. Common rejection reasons\n\nBe specific with numbers and thresholds. Output ONLY the checklist, no intro text.\nRespond in English.",
        "promptType": "define"
      },
      "typeVersion": 1.4
    },
    {
      "id": "cdeacd12-e90c-4f2f-a48c-8aeba1459ddd",
      "name": "Google Gemini for criteria generation",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        13184,
        6448
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-3-pro-preview"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0919c2f3-f3c1-4ba7-bd97-2c4a1d2823e8",
      "name": "Convert criteria and workflow to files",
      "type": "n8n-nodes-base.code",
      "position": [
        13536,
        6256
      ],
      "parameters": {
        "jsCode": "// Convert criteria text and workflow JSON to binary for Gemini Files API upload\nvar criteria = $input.first().json.response || $input.first().json.text || 'No criteria';\nvar formData = $('Submit workflow for review').first().json;\nvar workflowRaw = formData['Workflow JSON File'] || '{}';\nvar workflowJson = (typeof workflowRaw === 'object') ? JSON.stringify(workflowRaw, null, 2) : workflowRaw;\n\n// Output 2 items: criteria file + workflow file\nreturn [\n  {\n    json: { fileType: 'criteria' },\n    binary: {\n      data: await this.helpers.prepareBinaryData(\n        Buffer.from(criteria, 'utf-8'),\n        'criteria.txt',\n        'text/plain'\n      )\n    }\n  },\n  {\n    json: { fileType: 'workflow' },\n    binary: {\n      data: await this.helpers.prepareBinaryData(\n        Buffer.from(workflowJson, 'utf-8'),\n        'workflow.json',\n        'application/json'\n      )\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "45829ddd-8d34-47e8-b951-686a233e26d8",
      "name": "Upload files to Gemini Files API",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        13728,
        6256
      ],
      "parameters": {
        "url": "https://generativelanguage.googleapis.com/upload/v1beta/files",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "binaryData",
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Goog-Upload-Command",
              "value": "upload, finalize"
            },
            {
              "name": "X-Goog-Upload-Protocol",
              "value": "raw"
            }
          ]
        },
        "inputDataFieldName": "data",
        "nodeCredentialType": "googlePalmApi"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "20747a51-ce66-4592-b9f3-1ceb286bd77c",
      "name": "Combine uploaded file references",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        13904,
        6256
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "9b05dee3-812c-405c-a02e-082fc3651d72",
      "name": "Review workflow against criteria",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        14112,
        6256
      ],
      "parameters": {
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"contents\": [\n    {\n      \"parts\": [\n        {\n          \"fileData\": {\n            \"mimeType\": \"{{ $json.data[0].file.mimeType }}\",\n            \"fileUri\": \"{{ $json.data[0].file.uri }}\"\n          }\n        },\n        {\n          \"fileData\": {\n            \"mimeType\": \"{{ $json.data[1].file.mimeType }}\",\n            \"fileUri\": \"{{ $json.data[1].file.uri }}\"\n          }\n        },\n        {\n          \"text\": \"You are an expert n8n template marketplace reviewer.\\n\\nThe first file contains APPROVAL CRITERIA generated from official n8n Creator Hub guidelines.\\nThe second file contains the FULL WORKFLOW JSON to review.\\nTemplate Title: {{ $('Submit workflow for review').first().json['Template Title'] || 'N/A' }}\\n\\nReview the workflow against each criterion. For each: PASS or FAIL with specific reason.\\n\\nThen provide:\\n1. Overall Verdict: APPROVE / NEEDS WORK / REJECT\\n2. Score: X/100\\n3. Top 3 Critical Fixes (mention specific node names, settings)\\n4. Estimated Fix Time\\n\\nFORMAT INSTRUCTIONS:\\n- Output as a complete HTML email body using inline CSS only (no <style> blocks or external stylesheets).\\n- Use a clean, professional email layout with max-width: 640px, centered, white background.\\n- Use a colored header banner:\\n  - APPROVE: green (#22c55e)\\n  - NEEDS WORK: orange (#f59e0b)\\n  - REJECT: red (#ef4444)\\n- Render each criterion result as a row with \u2705 or \u274c and the reason.\\n- Use <table> for structured sections (good email client compatibility).\\n- Keep the total output concise but do NOT enforce a character limit.\\n- Do not include <html>, <head>, or <body> tags \u2014 output only the inner content suitable for embedding in an email body.\\n- Ensure all spacing, colors, and fonts are defined via inline style attributes.\"\n        }\n      ]\n    }\n  ]\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googlePalmApi"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "45aaf393-7da0-479c-9c68-b9aa5a5a64b0",
      "name": "Send review results via email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        14384,
        6256
      ],
      "parameters": {
        "sendTo": "={{ $('Submit workflow for review').first().json['Email Address'] }}",
        "message": "={{ $json.candidates[0].content.parts[0].text }}",
        "options": {},
        "subject": "=n8n Template Review: {{ $('Submit workflow for review').first().json['Template Title'] }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    }
  ],
  "connections": {
    "Generate Notion API URLs": {
      "main": [
        [
          {
            "node": "Fetch guideline pages from Notion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine all fetched pages": {
      "main": [
        [
          {
            "node": "Extract text from Notion blocks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Submit workflow for review": {
      "main": [
        [
          {
            "node": "Generate Notion API URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract text from Notion blocks": {
      "main": [
        [
          {
            "node": "Generate approval checklist with AI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine uploaded file references": {
      "main": [
        [
          {
            "node": "Review workflow against criteria",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Review workflow against criteria": {
      "main": [
        [
          {
            "node": "Send review results via email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload files to Gemini Files API": {
      "main": [
        [
          {
            "node": "Combine uploaded file references",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch guideline pages from Notion": {
      "main": [
        [
          {
            "node": "Combine all fetched pages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate approval checklist with AI": {
      "main": [
        [
          {
            "node": "Convert criteria and workflow to files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini for criteria generation": {
      "ai_languageModel": [
        [
          {
            "node": "Generate approval checklist with AI",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Convert criteria and workflow to files": {
      "main": [
        [
          {
            "node": "Upload files to Gemini Files API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}