{
  "id": "d8dMau1fQXnoMFRn",
  "name": "Notion & Airtable Stock Sync Validator with AI Slack Notifications",
  "tags": [],
  "nodes": [
    {
      "id": "00cc9d36-8dd3-4171-921d-350058ef05b9",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1360,
        80
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "849eb97d-f8c5-413f-b70e-53cd51a5b90c",
      "name": "Configure GPT-4o \u2013 Slack Summary Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
      "position": [
        1440,
        448
      ],
      "parameters": {
        "model": "gpt-4o",
        "options": {}
      },
      "credentials": {
        "azureOpenAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "273fc1a9-4d6d-48d5-b5a3-a114740555f9",
      "name": "Log Invalid Versioning Requests to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -16,
        464
      ],
      "parameters": {
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "db842a83-9e27-4c77-b87e-2ed44054aec5",
      "name": "Fetch Records from Airtable",
      "type": "n8n-nodes-base.airtable",
      "position": [
        -976,
        208
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appwM60t4Gdm6uXen",
          "cachedResultUrl": "https://airtable.com/appwM60t4Gdm6uXen",
          "cachedResultName": "Lead Manager"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblB6VXcwOisyAiM7",
          "cachedResultUrl": "https://airtable.com/appwM60t4Gdm6uXen/tblB6VXcwOisyAiM7",
          "cachedResultName": "Inventory"
        },
        "options": {},
        "operation": "search"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "735cb478-2746-40db-8230-c8cf178fb675",
      "name": "Fetch Records from Notion Database",
      "type": "n8n-nodes-base.notion",
      "position": [
        -976,
        -48
      ],
      "parameters": {
        "simple": false,
        "options": {},
        "resource": "databasePage",
        "operation": "getAll",
        "databaseId": {
          "__rl": true,
          "mode": "list",
          "value": "2b8802b9-1fa0-80e4-bfd3-df8d7e714ef8",
          "cachedResultUrl": "https://www.notion.so/2b8802b91fa080e4bfd3df8d7e714ef8",
          "cachedResultName": "Inventory Database"
        }
      },
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "3c09cacf-28e8-49a9-8221-0b5679ef1b3f",
      "name": "Merge Notion + Airtable Inputs",
      "type": "n8n-nodes-base.merge",
      "position": [
        -560,
        64
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "8ec9d617-34b9-46f1-bb98-67f7b768d81e",
      "name": "Build Combined Notion + Airtable Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        192,
        48
      ],
      "parameters": {
        "jsCode": "const notion = $items(\"Fetch Records from Notion Database\").map(i => i.json.properties);\nconst airtable = $items(\"Fetch Records from Airtable\").map(i => i.json);\n\nreturn [\n  { json: { notion, airtable } }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "b1a5772d-e0df-451e-821b-07bcd403eed1",
      "name": "Compare Notion Record With Airtable Record",
      "type": "n8n-nodes-base.code",
      "position": [
        464,
        48
      ],
      "parameters": {
        "jsCode": "// Input contains:\n// $json.notion = Notion item\n// $json.airtable = array of Airtable records\n\nconst notionItem = $json.notion[0];   // Just 1 item\nconst airtableItems = $json.airtable; // Multiple items in array\n\n// Extract the Notion fields\nconst notionName = notionItem[\"Item Name\"].title[0].plain_text;\nconst notionCount = notionItem[\"Quantity in Stock\"].number;\n\n// Find matching Airtable item by item name\nconst match = airtableItems.find(item => item[\"Item Name\"] === notionName);\n\nif (!match) {\n  return [{\n    json: {\n      status: \"no_match_found\",\n      item: notionName\n    }\n  }];\n}\n\nconst airtableCount = match[\"Quantity in Stock\"];\n\n// Compare both\nconst diff = notionCount - airtableCount;\n\nreturn [{\n  json: {\n    status: \"match_found\",\n    item: notionName,\n    notionCount,\n    airtableCount,\n    difference: diff,\n    airtableRecordId: match.id,\n    needsUpdate: diff !== 0\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e8f1ce9d-77b4-49dd-9664-2d3269d86bfc",
      "name": "Check If Record Requires Update",
      "type": "n8n-nodes-base.if",
      "position": [
        672,
        48
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "7a29bfaf-256c-43af-80a2-e25cfc958453",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.needsUpdate }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "1957c8d2-dccf-4b4d-b0e1-93133781aaea",
      "name": "Generate Slack Summary",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1440,
        240
      ],
      "parameters": {
        "text": "=Using the cycle count comparison result below, generate a concise Slack message for the operations team.\n\nData:\n{{ JSON.stringify($json, null, 2) }}\n\nRequirements:\n- If needsUpdate is false \u2192 send a friendly \u201cNo action needed\u201d message.\n- If needsUpdate is true \u2192 highlight mismatches clearly.\n- Keep message short, clean, and easy for a Slack channel.\n\nOutput only the final Slack-ready message.\n",
        "options": {
          "systemMessage": "=You are an AI assistant that generates short, clear Slack notifications for the operations team about inventory cycle counts.\n\nYour job:\n- Read the comparison result coming from Notion vs Airtable.\n- Explain whether the stock levels match or if there is a discrepancy.\n- If there is a difference, clearly state:\n  \u2022 Item name  \n  \u2022 Notion count  \n  \u2022 Airtable count  \n  \u2022 Difference  \n  \u2022 Required action  \n\n- If everything matches, send a simple \u201call good\u201d message.\n- Keep the tone simple, concise, and professional.\n- Output only the final Slack message (no explanations, no extra text).\n"
        },
        "promptType": "define"
      },
      "typeVersion": 2.1
    },
    {
      "id": "25cbfe5c-b0ba-4251-b7dd-a111b53050b0",
      "name": "Slack \u2013 Send Summary Notification",
      "type": "n8n-nodes-base.slack",
      "notes": "Sends formatted notification to #product-faqs channel with FAQ summary, category, and links",
      "position": [
        1872,
        240
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "user": {
          "__rl": true,
          "mode": "list",
          "value": "U09HMPVD466",
          "cachedResultName": "newscctv22"
        },
        "select": "user",
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "b5744459-52d9-460a-9007-b60bf9903501",
      "name": "Update Airtable Record With Corrected Count",
      "type": "n8n-nodes-base.airtable",
      "position": [
        1024,
        -672
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appwM60t4Gdm6uXen",
          "cachedResultUrl": "https://airtable.com/appwM60t4Gdm6uXen",
          "cachedResultName": "Lead Manager"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblB6VXcwOisyAiM7",
          "cachedResultUrl": "https://airtable.com/appwM60t4Gdm6uXen/tblB6VXcwOisyAiM7",
          "cachedResultName": "Inventory"
        },
        "columns": {
          "value": {
            "id": "={{$json.airtableRecordId}}",
            "Quantity in Stock": "={{ $json.notionCount }}"
          },
          "schema": [
            {
              "id": "id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "id",
              "defaultMatch": true
            },
            {
              "id": "Item Name",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Item Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "SKU",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "SKU",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Category",
              "type": "options",
              "display": true,
              "options": [
                {
                  "name": "Electronics",
                  "value": "Electronics"
                },
                {
                  "name": "Office Supplies",
                  "value": "Office Supplies"
                },
                {
                  "name": "Furniture",
                  "value": "Furniture"
                },
                {
                  "name": "Hardware",
                  "value": "Hardware"
                },
                {
                  "name": "Software",
                  "value": "Software"
                },
                {
                  "name": "Other",
                  "value": "Other"
                }
              ],
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Description",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Quantity in Stock",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Quantity in Stock",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Reorder Level",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Reorder Level",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Supplier Name",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Supplier Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Unit Cost",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Unit Cost",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Location",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Location",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Related Lead(s)",
              "type": "array",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Related Lead(s)",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Item Photo",
              "type": "array",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Item Photo",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date Added",
              "type": "dateTime",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Date Added",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "options",
              "display": true,
              "options": [
                {
                  "name": "Active",
                  "value": "Active"
                },
                {
                  "name": "Inactive",
                  "value": "Inactive"
                },
                {
                  "name": "Discontinued",
                  "value": "Discontinued"
                }
              ],
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "upsert"
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "9d4d02c6-222b-45f4-a751-0e91f4835312",
      "name": "Configure GPT-4o \u2013 Slack Summary Model2",
      "type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
      "position": [
        1408,
        -64
      ],
      "parameters": {
        "model": "gpt-4o",
        "options": {}
      },
      "credentials": {
        "azureOpenAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "351bfd10-098a-4725-8681-be8f919c8ed1",
      "name": "Generate Slack Summary1",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1408,
        -272
      ],
      "parameters": {
        "text": "=Using the cycle count comparison result below, generate a concise Slack message informing the operations team that a stock discrepancy was found and Airtable has been updated.\n\nData:\n{{ JSON.stringify($json, null, 2) }}\n\nInstructions:\n- Highlight the item name.\n- Show the physical (Notion) count and old Airtable count.\n- Show the difference.\n- State clearly that Airtable has been updated to match the physical count.\n- Keep the message short and team-friendly.\n",
        "options": {
          "systemMessage": "=You are an AI assistant that writes short, clear Slack notifications for the operations team regarding inventory cycle count updates.\n\nYour job:\n- Report mismatches between Notion physical counts and Airtable system counts.\n- Clearly show item name, counted quantity, system quantity, and difference.\n- Confirm that Airtable has been updated with the corrected count.\n- Keep the message concise, professional, and easy to read.\n\nDo NOT add unnecessary text or emojis unless helpful for clarity.\n"
        },
        "promptType": "define"
      },
      "typeVersion": 2.1
    },
    {
      "id": "b4440e05-3ae3-42fe-9009-cd8fb59976be",
      "name": "Slack \u2013 Send Update Notification",
      "type": "n8n-nodes-base.slack",
      "notes": "Sends formatted notification to #product-faqs channel with FAQ summary, category, and links",
      "position": [
        1840,
        -272
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "user": {
          "__rl": true,
          "mode": "list",
          "value": "U09HMPVD466",
          "cachedResultName": "newscctv22"
        },
        "select": "user",
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "fd724c3b-7ec0-40c7-b887-425bf85eb9a4",
      "name": "Validate Payload Structure",
      "type": "n8n-nodes-base.if",
      "position": [
        -208,
        64
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "e2adb005-2b3c-4d1e-8445-442df1fe925a",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.id }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "7f9e744f-e1a8-4a7f-8708-23c19c7ba303",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2208,
        -1056
      ],
      "parameters": {
        "width": 848,
        "height": 592,
        "content": "## \ud83d\udd04 Notion & Airtable Stock Sync Validator with AI Slack Notifications\n\nThis workflow compares item-wise inventory counts between Notion (physical count) and \nAirtable (system count) to ensure accurate stock reconciliation. Once triggered manually, \nthe workflow fetches one Notion record and multiple Airtable records, merges both datasets, \nand performs a one-to-one comparison. If both systems match, a Slack summary confirms \n\u201cNo action needed\u201d. If differences exist, the workflow updates Airtable with the correct \nquantity and sends a discrepancy alert to the operations team. All invalid or malformed \npayloads are logged into Google Sheets for review.\n\n### \ud83d\udd39 Workflow Overview\n1. Manual trigger \u2192 Fetch Notion & Airtable records  \n2. Merge both datasets into a unified payload  \n3. Validate payload structure (id, item fields)  \n4. Compare counts and detect discrepancies  \n5. If mismatch \u2192 update Airtable with correct quantity  \n6. Based on result \u2192 generate Slack summary (Updated / No Action Needed)  \n7. Send Slack notification to operations  \n8. Log invalid requests into Google Sheets  \n\n### \ud83d\udd39 Tools & Integrations\n\u2022 Notion \u2192 Physical inventory count  \n\u2022 Airtable \u2192 System inventory record  \n\u2022 GPT-4o \u2192 Slack message generation  \n\u2022 Google Sheets \u2192 Invalid log  \n\u2022 Slack \u2192 Ops alerts\n"
      },
      "typeVersion": 1
    },
    {
      "id": "624f8ae8-a231-4ced-a048-fabd947e78be",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1184,
        -192
      ],
      "parameters": {
        "color": 7,
        "width": 1328,
        "height": 832,
        "content": "## Data Intake & Merge \u2014 Notion + Airtable\nFetches physical count from Notion and system count from Airtable.\nMerges both datasets into a single comparison-ready payload.\nInvalid or incomplete structures are logged to Google Sheets.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "201e9850-99b9-4064-9550-89bec0361702",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        160,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 560,
        "content": "## Reconciliation Logic \u2014 Count Comparison\nCompares physical stock from Notion with system stock from Airtable.\nDetects mismatches, calculates the difference, and determines if a record requires correction.\nIf counts match \u2192 workflow branches to \u201cNo Action Needed\u201d.\nIf mismatch \u2192 marks item for system update.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "8f616b82-d05b-42f3-9d24-612c6e7c8978",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        944,
        -928
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 464,
        "content": "## System Update \u2014 Fixing Airtable Record\nWhen discrepancies are detected, updates the Airtable record with the corrected physical count.\nEnsures the system-of-record stays accurate for reporting and inventory control.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "7e9051c3-67ab-4052-90ff-9c3964339ef6",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1296,
        -416
      ],
      "parameters": {
        "color": 7,
        "width": 912,
        "height": 1008,
        "content": "## Notification Engine \u2014 AI Slack Messaging\nUses GPT-4o to generate short, clear Slack messages for inventory reconciliation.\nIf no update needed \u2192 sends a \u201ccounts match\u201d summary.  \nIf Airtable updated \u2192 sends a discrepancy alert summarizing Notion count, Airtable count, and difference.\nAll notifications go to the operations user for real-time visibility.\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "f601cfda-3a6c-4681-a3e8-bd1a1d70eee7",
  "connections": {
    "Generate Slack Summary": {
      "main": [
        [
          {
            "node": "Slack \u2013 Send Summary Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Slack Summary1": {
      "main": [
        [
          {
            "node": "Slack \u2013 Send Update Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Payload Structure": {
      "main": [
        [
          {
            "node": "Build Combined Notion + Airtable Payload",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Invalid Versioning Requests to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Records from Airtable": {
      "main": [
        [
          {
            "node": "Merge Notion + Airtable Inputs",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Notion + Airtable Inputs": {
      "main": [
        [
          {
            "node": "Validate Payload Structure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check If Record Requires Update": {
      "main": [
        [
          {
            "node": "Update Airtable Record With Corrected Count",
            "type": "main",
            "index": 0
          },
          {
            "node": "Generate Slack Summary1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Generate Slack Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Records from Notion Database": {
      "main": [
        [
          {
            "node": "Merge Notion + Airtable Inputs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Fetch Records from Notion Database",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Records from Airtable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Combined Notion + Airtable Payload": {
      "main": [
        [
          {
            "node": "Compare Notion Record With Airtable Record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configure GPT-4o \u2013 Slack Summary Model": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Slack Summary",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Configure GPT-4o \u2013 Slack Summary Model2": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Slack Summary1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Compare Notion Record With Airtable Record": {
      "main": [
        [
          {
            "node": "Check If Record Requires Update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}