{
  "id": "5DDtEV5rA1bpu9JO",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Generate AI-friendly product descriptions from Airtable to Airtable",
  "tags": [],
  "nodes": [
    {
      "id": "11133939-8502-4868-9120-01541818aed3",
      "name": "Every 15 Minutes",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -96,
        -80
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "63af4aa5-1ca5-499e-b258-1d5d95d00523",
      "name": "Fetch Pending Products",
      "type": "n8n-nodes-base.airtable",
      "position": [
        128,
        -80
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "app28w1Y5KEHUZEwl",
          "cachedResultUrl": "https://airtable.com/app28w1Y5KEHUZEwl",
          "cachedResultName": "Products"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblGyulGNkzj5iC7R",
          "cachedResultUrl": "https://airtable.com/app28w1Y5KEHUZEwl/tblGyulGNkzj5iC7R",
          "cachedResultName": "Table 1"
        },
        "options": {},
        "operation": "search"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "e4fd95a6-2fcf-4732-8d36-56814dfe0d9a",
      "name": "Split Into Batches",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        304,
        -80
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "43931121-3745-4070-be97-48732aac01ef",
      "name": "Parse AI JSON",
      "type": "n8n-nodes-base.code",
      "position": [
        960,
        -64
      ],
      "parameters": {
        "jsCode": "const items = [];\n\nfor (const item of $input.all()) {\n  const output = item.json.output;\n  \n  // Extract the record ID from the input item\n  // Assuming the record ID comes from a previous Airtable node\n  const recordId = $('Split Into Batches').first().json.id || \"Unknown\" ;\n  \n  // Convert bullet features array to newline-separated string\n  const bulletFeaturesText = output.ai_bullet_features.join('\\n');\n  \n  // Convert feature table to JSON string\n  const featureTableJson = JSON.stringify(output.ai_feature_table);\n  \n  items.push({\n    json: {\n      id: recordId,\n      ai_long_description: output.ai_long_description,\n      ai_short_answer_block: output.ai_short_answer_block,\n      ai_bullet_features: bulletFeaturesText,\n      ai_feature_table_json: featureTableJson,\n      ai_status: 'done',\n      ai_last_run_at: $now.toISO()\n    },\n    pairedItem: item.pairedItem\n  });\n}\n\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "d8114e15-cd5c-4684-b2c0-830fae0d25fb",
      "name": "Prepare Airtable Update Data",
      "type": "n8n-nodes-base.set",
      "position": [
        1264,
        -64
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "a1b2c3d4",
              "name": "id",
              "type": "string",
              "value": "={{ $json.id }}"
            },
            {
              "id": "b2c3d4e5",
              "name": "ai_long_description",
              "type": "string",
              "value": "={{ $json.ai_long_description }}"
            },
            {
              "id": "c3d4e5f6",
              "name": "ai_short_answer_block",
              "type": "string",
              "value": "={{ $json.ai_short_answer_block }}"
            },
            {
              "id": "d4e5f6a7",
              "name": "ai_bullet_features",
              "type": "string",
              "value": "={{ $json.ai_bullet_features }}"
            },
            {
              "id": "e5f6a7b8",
              "name": "ai_feature_table_json",
              "type": "string",
              "value": "={{ $json.ai_feature_table_json }}"
            },
            {
              "id": "f6a7b8c9",
              "name": "status",
              "type": "string",
              "value": "Done"
            },
            {
              "id": "a7b8c9d0",
              "name": "ai_last_run_at",
              "type": "string",
              "value": "={{ $now }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "5acba58d-fe30-4506-a755-a8c9542353fb",
      "name": "Update Product In Airtable",
      "type": "n8n-nodes-base.airtable",
      "position": [
        1568,
        -64
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "app28w1Y5KEHUZEwl",
          "cachedResultUrl": "https://airtable.com/app28w1Y5KEHUZEwl",
          "cachedResultName": "Products"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblGyulGNkzj5iC7R",
          "cachedResultUrl": "https://airtable.com/app28w1Y5KEHUZEwl/tblGyulGNkzj5iC7R",
          "cachedResultName": "Table 1"
        },
        "columns": {
          "value": {
            "id": "={{ $json.id }}",
            "Status": "={{ $json.status }}",
            "ai_last_run_at": "={{ $json.ai_last_run_at }}",
            "ai_bullet_features": "={{ $json.ai_bullet_features }}",
            "ai_long_description": "={{ $json.ai_long_description }}",
            "ai_feature_table_json": "={{ $json.ai_feature_table_json }}",
            "ai_short_answer_block": "={{ $json.ai_short_answer_block }}"
          },
          "schema": [
            {
              "id": "id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "id",
              "defaultMatch": true
            },
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Product Name",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Product Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Brand",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Brand",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Category",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Price",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Price",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Currency",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Currency",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Geo Region",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Geo Region",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Color",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Color",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Size",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Size",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Material",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Material",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ai_long_description",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "ai_long_description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ai_short_answer_block",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "ai_short_answer_block",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ai_bullet_features",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "ai_bullet_features",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ai_feature_table_json",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "ai_feature_table_json",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ai_last_run_at",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "ai_last_run_at",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "3d4ad36b-542c-478c-b5a9-9e7799610f1f",
      "name": "OpenAI Chat Model - GPT-4o-mini",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        576,
        160
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "gpt-4o-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "280aae8c-cb2f-4dff-8b67-a4a82b42a2a6",
      "name": "Memory - Conversation Buffer",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        688,
        144
      ],
      "parameters": {
        "sessionKey": "\"GEO Session e-commerce\"",
        "sessionIdType": "customKey"
      },
      "typeVersion": 1.3
    },
    {
      "id": "0888441d-f52b-4a89-a3e8-abcd23e1cea6",
      "name": "Output Parser - Structured JSON",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        816,
        144
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"ai_long_description\": \"This cotton crewneck T-shirt by UrbanWear is designed for everyday comfort in warm climates. Made from 100% soft and breathable cotton, it offers a relaxed fit and durable stitching suitable for casual wear, travel, or layering. The fabric helps absorb moisture, keeping the wearer cool throughout the day.\",\n  \"ai_short_answer_block\": \"A lightweight and breathable cotton T-shirt from UrbanWear, designed for daily comfort in warm weather. It features durable stitching, a classic crewneck style, and is ideal for casual or layered outfits.\",\n  \"ai_bullet_features\": [\n    \"100% soft and breathable cotton\",\n    \"Relaxed fit with classic crewneck\",\n    \"Ideal for daily wear and warm weather\"\n  ],\n  \"ai_feature_table\": [\n    { \"label\": \"Material\", \"value\": \"100% Cotton\" },\n    { \"label\": \"Fit Type\", \"value\": \"Relaxed\" },\n    { \"label\": \"Neck Style\", \"value\": \"Crewneck\" },\n    { \"label\": \"Suitable For\", \"value\": \"Casual & Daily Wear\" }\n  ]\n}\n"
      },
      "typeVersion": 1.3
    },
    {
      "id": "7746b503-e560-46d1-bd47-4589de72d15f",
      "name": "AI Agent - GEO Analyzer",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        624,
        -64
      ],
      "parameters": {
        "text": "=Product data:\n- Name:{{ $json['Product Name'] }}\n- Brand: {{ $json.Brand }}\n- Category: {{ $json.Brand }}\n- Price: {{ $json.Price }}\n- Target region: {{ $json['Geo Region'] }}\n- Existing description (if any):None\n- Key attributes: color: {{ $json.Color }}, size: {{ $json.Size }}, material: {{ $json.Material }}\nGuidelines:\n- Language: English\n- Tone: clear, factual, benefit-focused.\n- Make content easy for AI Answer Engines to use: short, structured, unambiguous.\n- Do NOT hallucinate impossible specs; only infer what is normal/common for this category.\n- Output VALID JSON only. No explanation, no extra text.\n",
        "options": {
          "systemMessage": "=You are an eCommerce product content specialist.\n\nYour job:\n1. Create AI-friendly product content that works well in AI Overviews and answer engines.\n2. Always return STRICT JSON, no markdown.\n\nReturn this JSON schema:\n{\n  \"ai_long_description\": \"string\",\n  \"ai_short_answer_block\": \"string (2\u20133 sentences, factual tone)\",\n  \"ai_bullet_features\": [\"string\", \"string\", \"string\"],\n  \"ai_feature_table\": [\n    { \"label\": \"string\", \"value\": \"string\" }\n  ]\n}\n"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.1
    },
    {
      "id": "0d9fbb62-70dc-4d8f-8b36-39a892d4d4cb",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -224,
        -1024
      ],
      "parameters": {
        "width": 480,
        "height": 784,
        "content": "## AI-Friendly Product Description Generator \u2013 Overview\n\nThis workflow creates high-quality, structured product descriptions using AI. It checks Airtable every 15 minutes for products marked as pending, generates long descriptions, short answer blocks, bullet features, and feature table content, and updates the same Airtable record. This helps eCommerce teams maintain consistent, SEO-friendly product copy with minimal effort.\n\n### How it works\n1. A schedule trigger runs every 15 minutes.\n2. Airtable returns products with status = \u201cpending\u201d.\n3. Products are processed in small batches.\n4. GPT-4o-mini generates structured JSON content for each product.\n5. A structured output parser ensures clean, valid JSON.\n6. A Code node converts AI output into Airtable-safe fields.\n7. Airtable is updated with the generated descriptions and marked \u201cdone\u201d.\n\n### Setup steps\n- Add Airtable credentials and connect to your Product table.\n- Add OpenAI credentials.\n- Confirm Airtable fields match:  \n  `ai_long_description`,  \n  `ai_short_answer_block`,  \n  `ai_bullet_features`,  \n  `ai_feature_table_json`,  \n  `status`,  \n  `ai_last_run_at`.\n- Run a test execution to verify results.\n\n### Customization\nAdjust frequency, tone, prompt style, or field mappings as needed.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "3d1f16aa-9d98-4835-b92f-b808c800d8bc",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -224,
        -192
      ],
      "parameters": {
        "color": 2,
        "width": 464,
        "height": 272,
        "content": "## Trigger & Fetch Products\nRuns every 15 minutes and pulls pending product records from Airtable.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "116c670f-d97d-4d54-a2a4-2c39562a6ae0",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        256,
        -192
      ],
      "parameters": {
        "color": 2,
        "height": 336,
        "content": "## Batch Processing\nSplits products into batches to avoid API rate limits.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "8a5b6e21-9350-4faf-9514-08bead79973f",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        528,
        -160
      ],
      "parameters": {
        "color": 2,
        "width": 560,
        "height": 464,
        "content": "## AI Content Generation\nGenerates product descriptions and structured content using GPT-4o-mini.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "42ea25a7-910f-4b7e-b67b-152a67f4f77c",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1184,
        -160
      ],
      "parameters": {
        "color": 2,
        "height": 304,
        "content": "## Format AI Output\nConverts structured JSON into clean Airtable fields.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "35403fb3-67da-4978-b965-22ad5e77d27a",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1488,
        -176
      ],
      "parameters": {
        "color": 2,
        "height": 320,
        "content": "## Update Airtable\nUpdates product records with AI output and marks them as completed.\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "cc6c0bcc-4035-4410-90ed-a83cf837f417",
  "connections": {
    "Parse AI JSON": {
      "main": [
        [
          {
            "node": "Prepare Airtable Update Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every 15 Minutes": {
      "main": [
        [
          {
            "node": "Fetch Pending Products",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Into Batches": {
      "main": [
        [],
        [
          {
            "node": "AI Agent - GEO Analyzer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Pending Products": {
      "main": [
        [
          {
            "node": "Split Into Batches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent - GEO Analyzer": {
      "main": [
        [
          {
            "node": "Parse AI JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Product In Airtable": {
      "main": [
        [
          {
            "node": "Split Into Batches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Memory - Conversation Buffer": {
      "ai_memory": [
        [
          {
            "node": "AI Agent - GEO Analyzer",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Airtable Update Data": {
      "main": [
        [
          {
            "node": "Update Product In Airtable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model - GPT-4o-mini": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent - GEO Analyzer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Output Parser - Structured JSON": {
      "ai_outputParser": [
        [
          {
            "node": "AI Agent - GEO Analyzer",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    }
  }
}